Is there any way to get subrecord without unsafeCoerce? - purescript

type Foo = {
x :: Int,
y :: Int
}
type Bar = {
x :: Int
}
foo :: Foo
foo = {x:1,y:2}
bar :: Bar
bar = foo
Could not match type
()
with type
( y :: Int
)
Why isn't this possible?
I cannot use
type Bar a = {
x :: Int | a
}
And don't want to recreate the record
bar = {x : foo.x}
Is unsafeCoerce the only option?
What could be the problem with use of unsafeCoerce?

If you want to write functions deal with records that only have some specific fields then you can use an open row instead:
bar :: forall r. { x :: Int | r } -> { x :: Int | r }
bar rec = rec
That will still be compatible with both Foo and Bar synonyms.
However, if that's no good either and you do just want to "forget" about y then unsafeCoerce should be okay to use, as long as you only ever coerce Foo to Bar, and not back again.
I'd strongly recommend aliasing unsafeCoerce rather than using it directly though:
forget :: Foo -> Bar
forget = Unsafe.Coerce.unsafeCoerce
It's easy to make mistakes when using it directly even when you think you are using it in a principled way.
Personally, I'd just make a new record without the fields, even though it involves some boilerplate.

Related

Purescript types for buildQueryString function

I am new to Purescript and I am trying to write a function that
can take any record value and iterate over the fields and values and build
a querystring.
I am thinking something like:
buildQueryString :: forall a. PropertyTraversible r => r -> String
which I want to use like this:
buildQueryString {name: "joe", age: 10} -- returns: "name=joe&age=10"
Is there a way to write something like that in Purescript with existing idioms or do I have to create my own custom Type Class for this?
I'm sure that it can be shorter, but here is my implementation based on purescript-generic-rep (inspired by genericShow). This solution uses typeclasses - it seems to be standard approach with generic-rep:
module Main where
import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Data.Foldable (intercalate)
import Data.Generic.Rep (class Generic, Constructor(..), Field(..), Product(..), Rec(..), from)
import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol)
class EncodeValue a where
encodeValue ∷ a → String
instance encodeValueString ∷ EncodeValue String where
encodeValue = id
instance encodeValueInt ∷ EncodeValue Int where
encodeValue = show
class EncodeFields a where
encodeFields :: a -> Array String
instance encodeFieldsProduct
∷ (EncodeFields a, EncodeFields b)
⇒ EncodeFields (Product a b) where
encodeFields (Product a b) = encodeFields a <> encodeFields b
instance encodeFieldsField
∷ (EncodeValue a, IsSymbol name)
⇒ EncodeFields (Field name a) where
encodeFields (Field a) =
[reflectSymbol (SProxy :: SProxy name) <> "=" <> encodeValue a]
buildQueryString
∷ ∀ a l n.
Generic n (Constructor l (Rec a))
⇒ (EncodeFields a)
⇒ n
→ String
buildQueryString n =
build <<< from $ n
where
build (Constructor (Rec fields)) = intercalate "&" <<< encodeFields $ fields
newtype Person =
Person
{ name ∷ String
, age ∷ Int
}
derive instance genericPerson ∷ Generic Person _
joe ∷ Person
joe = Person { name: "joe", age: 10 }
main :: forall e. Eff (console :: CONSOLE | e) Unit
main = do
log <<< buildQueryString $ joe
buildQueryString expects value of type with single constructor which contains a record (possibly just newtype) because it is impossible to derive a Generic instance for "unwrapped" Record type.
If you want to handle also Array values etc. then encodeValue should probably return values of type Array String.
This is possible with purescript-generics but it only works on nominal types, not on any record. But it saves you boilerplate, since you can just derive the instance for Generic, so it would work with any data or newtype without further modification.
Downside is, you have to make some assumptions about the type: like it only contains one record and the record does not contain arrays or other records.
Here is a hacky demonstration how it would work:
data Person = Person
{ name :: String
, age :: Int
}
derive instance genericPerson :: Generic Person
joe = Person { name: "joe", age: 10 }
build :: GenericSpine -> String
build (SRecord arr) = intercalate "&" (map (\x -> x.recLabel <> "=" <> build (x.recValue unit)) arr)
build (SProd _ arr) = fromMaybe "TODO" $ map (\f -> build (f unit)) (head arr)
build (SString s) = s
build (SInt i) = show i
build _ = "TODO"
test = build (toSpine joe)
purescript-generics-rep is newer, so possibly there is a better solution, maybe even on any record. I have not tried that (yet).

Proving properties of sets in Inox/Welder

I would like to proof some properties of sets on Inox/Welder but I'm lacking examples that help me to figure out how to do so. Say I want to proof:
content(y::xs).contains(x) && x != y ==> content(xs).contains(x)
I define the property:
def property(xs: Expr) =
forall("x"::A,"y"::A){case (x,y)
content(ConsA(y,xs)).contains(x) && x !== y ==> content(xs).contains(x)
}
But it turns out that this property will not compile because it is not well formulated (apparently the wrong parts are .contains, &&, !==...)
So, what is the right way to formulate the property?. Here I'm assuming I have a content function defined as:
val contentFunction = mkFunDef(contentID)("A") { case Seq(aT) => (
Seq("l" :: T(list)(aT)), SetType(aT), { case Seq(l) =>
if_ (l.isInstOf(T(cons)(aT))) {
SetAdd(E(contentID)(aT)(l.asInstOf(T(cons)(aT)).getField(tail)), l.asInstOf(T(cons)(aT)).getField(head))
} else_ {
FiniteSet(Seq.empty, aT)
}
})
}
Regarding the proof part imagine I'm given the function:
def without(x: A, xs: List[A]) = xs match{
case Nil() => Nil()
case y :: ys if(x == y) => without(x,ys)
case y :: ys if(x != y) => y :: without(x,ys)
}
that should remove x from list xs and say I want to proof that
content(without(x,l)) == content(l) -- Set(x)
Can you give a sketch of how to do it? Should I be using the BuiltInNames such as SetDifference?
Compilation Error
The compilation error you are seeing might come from the missing arrow after the case in your formulation of the property. Also, be sure to use the right identifier for content.
def property(xs: Expr) =
forall("x"::A,"y"::A) { case (x,y) =>
contentID(ConsA(y,xs)).contains(x) && x !== y ==> contentID(xs).contains(x)
}
Otherwise, the property looks correctly encoded using the Inox DSL.
Proof of Property
Regarding the proof itself, it don't think the without function is entirely necessary. The proof should go smoothly by structural induction on the list xs.
structuralInduction(property(_), "xs" :: T(List)(A)) { case (ihs, goal) =>
ihs.expression match {
case C(`Cons`, head, tail) => // Your case for Cons here.
case C(`Nil`) => // Your case for Nil here.
}
}
The ListAndTrees showcases many such proofs.
BuiltInName
Regarding the BuiltInNames class, it is used in the parser of Inox expressions that is currently being developed within Welder. This very likely will be put in a separate project very soon.
This parser is used within Welder so that you can write Inox expressions using a somewhat friendlier syntax. For instance, your property could be stated as:
def property(xs: Expr) =
e"forall x, y. contains(content(Cons(y, $xs)), x) && x != y ==> contains(content($xs), x)"
Inox Expressions
One last point. If you are looking for an exhaustive list of the different constructs available in Inox, have a look at the Expressions file in Inox.

Purescript Reuse Argonaut JSON Decoding for Affjax Respondeable

I'm trying to fetch some JSON data from a Haskell server, but I'm having trouble with the Respondeable instance, as well as just Affjax in general. I've defined EncodeJson + DecodeJson with Data.Argonaut.Generic.Aeson (GA), but I can't figure out how to fit that in with the Respondeable instance and it's fromResponse function.
It gives me the error "Could not match type Foreign with type Json" but is it possible to reuse my decodeJson instance without having to create anything else by hand? Maybe by creating an IsForeign instance, but using GA.decodeJson in that? I'm just not sure how to go about doing it. I've seen how it's done in https://github.com/purescript/purescript-foreign/blob/master/examples/Complex.purs by hand, but I have complex types that need to match up with my Haskell JSON output, and it's going to be a huge pain to do it manually.
I'm using purescript 10.7, Affjax 3.02, and argonaut 2.0.0, and argonaut-generic-codecs 5.1.0. Thanks!
testAffjax :: forall eff. Aff (ajax :: AJAX | eff) (Answer)
testAffjax = launchAff do
res <- affjax $ defaultRequest { url = "/", method = Left GET }
pure res.response
data Answer = Answer {
_answer :: String
, _isCorrect :: Boolean
, _hint :: String
}
{- PROBLEM -}
instance respondableAnswer :: Respondable Answer where
responseType = Tuple Nothing JSONResponse
fromResponse = GA.decodeJson {- Error here -}
derive instance genericAnswer :: Generic Answer
instance showAnswer :: Show Answer where
show = gShow
instance encodeAnswer :: EncodeJson Answer where
encodeJson = GA.encodeJson
instance decodeAnswer :: DecodeJson Answer where
decodeJson = GA.decodeJson
What you're looking for is a function that adapts a JSON decoder:
decodeJson :: forall a. Json -> Either String a
To return using F rather than Either. F is a synonym defined in Data.Foreign for Except MultipleErrors a. To do that we need to:
Translate our String error into a MultipleErrors
Convert from Either to Except
MultipleErrors is another synonym defined in Data.Foreign, this time for NonEmptyList ForeignError. Looking at ForeignError there's a constructor also called ForeignError that lets us provide some string message. That leaves us with the need to create a NonEmptyList, which is pretty easy:
remapError = pure <<< ForeignError
NonEmptyList is Applicative, so we can create a one-element list with pure.
To go from Either to Except is also straightforward. Again looking at the definitions in Pursuit we can see:
newtype ExceptT m e a = ExceptT (m (Either e a))
type Except = ExceptT Identity
So ExceptT is just a fancy Either already, giving us:
eitherToExcept = ExceptT <<< pure
The pure here is to lift Either e a into m (Either e a), which for Except m ~ Identity.
So now we can take this stuff, and make a general "decode JSON for Affjax responses" function:
decodeJsonResponse :: forall a. DecodeJson a => Json -> F a
decodeJsonResponse =
ExceptT <<< pure <<< lmap (pure <<< ForeignError) <<< decodeJson
The only other thing that happened in here is we used lmap to map over the left part of the Either, to do the error-message-type-conversion bit.
We can now use Kleisli composition ((<=<)) to chain this decodeJsonResponse together with the original fromResponse that will do the initial ResponseContent -> F Json:
instance respondableAnswer :: Respondable Answer where
responseType = Tuple (Just applicationJSON) JSONResponse
fromResponse = decodeJsonResponse <=< fromResponse
Here's the full example using your Answer type:
module Main where
import Prelude
import Control.Monad.Aff (Aff)
import Control.Monad.Except (ExceptT(..))
import Data.Argonaut (class DecodeJson, class EncodeJson, Json, decodeJson)
import Data.Argonaut.Generic.Argonaut as GA
import Data.Bifunctor (lmap)
import Data.Foreign (F, ForeignError(..))
import Data.Generic (class Generic, gShow)
import Data.Maybe (Maybe(..))
import Data.MediaType.Common as MediaType
import Data.Tuple (Tuple(..))
import Network.HTTP.Affjax as AX
import Network.HTTP.Affjax.Response as AXR
testAffjax :: forall eff. Aff (ajax :: AX.AJAX | eff) Answer
testAffjax = _.response <$> AX.get "/"
newtype Answer = Answer
{ _answer :: String
, _isCorrect :: Boolean
, _hint :: String
}
derive instance genericAnswer :: Generic Answer
instance showAnswer :: Show Answer where
show = gShow
instance encodeAnswer :: EncodeJson Answer where
encodeJson = GA.encodeJson
instance decodeAnswer :: DecodeJson Answer where
decodeJson = GA.decodeJson
instance respondableAnswer :: AXR.Respondable Answer where
responseType = Tuple (Just MediaType.applicationJSON) AXR.JSONResponse
fromResponse = decodeJsonResponse <=< AXR.fromResponse
decodeJsonResponse :: forall a. DecodeJson a => Json -> F a
decodeJsonResponse =
ExceptT <<< pure <<< lmap (pure <<< ForeignError) <<< decodeJson

What is the idiomatic way to work with inheritance from javascript?

For example javascript library has this hierarchy
class Base
class Foo:Base
class Bar:Base
and this function
calc(x:Base) : Int
calc(new Bar())
How do you write this function in PureScript?
foreign import calc :: ??? -> Int
I think it depends on what do you want to do with these classes. I would do something like this:
-- purs file
foreign import data Base :: *
foreign import data Foo :: *
foreign import data Bar :: *
fooToBase :: Foo -> Base
fooToBase = unsafeCoerce
barToBase :: Bar -> Base
barToBase = unsafeCoerce
foreign import newFoo :: forall e. Eff e Foo
foreign import newBar :: forall e. Eff e Bar
-- works with all ancestors
foreign import calc :: Base -> Eff e Unit
-- works only with Foos
foreign import fooMethod :: String -> Foo -> Eff e Int
-- using
main = do
foo <- newFoo
bar <- newBar
calc $ fooToBase foo
calc $ barToBase bar
fooMethod "test" foo
-- js file
exports.newFoo = function() { return new Foo(); };
exports.newBar = function() { return new Bar(); };
exports.calc = function(o) {
return function() {
return o.calc();
};
};
exports.fooMethod = function(str) {
return function(o) {
return function() {
return o.fooMethod();
};
};
};
Everything here should live in Eff probably, because making new instances changes global state.

How to check if a character is contained in string?

I want to check if the string contains the character. I am writing a hangman code.
For example, here is the word to guess: "scala", but it looks like "_ _ _ _ _" tho the user. Let's assume that user inputs letter 'a', then it must look like "_ _ a _ a".
def checkGuess(){
if (result.contains(user_input)) {
val comp = result.toCharArray
for (i <- comp){
if (user_input != comp(i))
comp(i) = '_'
comp(i)
}
val str = comp.toString
}
}
Is this right?
Thank you in advance.
I don't think this is homework, so I'll probably regret answering if it is...
case class HangmanGame(goal: String, guesses: Set[Char] = Set.empty[Char]) {
override def toString = goal map {c => if (guesses contains c) c else '_'} mkString " "
val isComplete = goal forall { guesses.contains }
def withGuess(c: Char) = copy(guesses = guesses + c)
}
Then
val h = HangmanGame("scala")
h: HangmanGame = _ _ _ _ _
scala> val h1 = h.withGuess('a')
h1: HangmanGame = _ _ a _ a
scala> val h2 = h1.withGuess('l')
h2: HangmanGame = _ _ a l a
scala> val h3 = h2.withGuess('s')
h3: HangmanGame = s _ a l a
scala> val h4 = h3.withGuess('c')
h4: HangmanGame = s c a l a
scala> h4.isComplete
res5: Boolean = true
UPDATE
Okay, so it does look like homework. I guess the genie's out of the bottle now, but unless you get up to speed on Scala very quickly you're going to have a really hard time explaining how it works.
How about:
scala> def checkGuess(str: String, c: Char) = str.replaceAll("[^"+c+"]","_")
checkGuess: (str: String,c: Char)java.lang.String
scala> checkGuess("scala",'a')
res1: java.lang.String = __a_a
scala> def checkGuess2(str: String, C: Char) = str map { case C => C; case _ => '_'}
checkGuess2: (str: String,C: Char)String
scala> checkGuess2("scala",'a')
res2: String = __a_a
Here are some comments about how you wrote this. When using this syntax, def checkGuess() { ... }, the function will not return any value, it will return Unit instead.
This means that you're using it for its side effect only (such as setting some var outside the code block or printing some values). The issue is that you are not setting any value or printing anything inside the function (no printing, no assignment).
What you don't show in your code snippet is where you store the string to guess, the user input and the feedback to print. You can pass the first two as arguments and the last one as a returned value. This make the input and output self contained in the function and does not presume where you render the feedback.
def feedback(target:String, guesses:String): String = {
// target is the string to guess like "scala"
// guesses are the letters that have been provided so far, like "ac"
// last expression should be the feedback to print for instance "_ca_a"
}
Then you can think about the function as transforming each letter in target with _ or with itself depending on whether it is contained in guesses. For this the target map { c => expr } would work pretty well if you figure out how to make expr return c if c is in guesses and '_' otherwise.
Staying as close as possible to the main question ( How to check if a character is contained in string? ) what I did was changing the approach, i.e.:
Inside a for loop, I wanted to do something like some_chr == 't'
and I did the following some_chr.toString == "t" and it worked just fine.