I am writing bindings from History.js into PureScript and still struggling to understand the Eff monad, what a row of effects are and why they are valuable. Right now I have the following written with EasyFFI
type Title = String
type Url = String
type State = forall a. {title :: Title, url :: Url | a}
type Data = forall a. { | a}
type StateUpdater = Data -> Title -> Url -> Unit
-- this function is a work around for 'data' as a reserved word.
foreign import getData
"function getData(state){ return state['data']; }"
:: forall a b. { | a} -> b
unwrapState :: StateUpdater -> State -> Unit
unwrapState f s = f (getData s) s.title s.url
replaceState' :: StateUpdater
replaceState' = unsafeForeignProcedure ["data","title","url"] "History.replaceState(data,title,url)"
replaceState :: State -> Unit
replaceState = unwrapState replaceState'
foreign import data BeforeEach :: !
beforeEach :: forall e a. Eff e a -> Eff (beforeEach :: BeforeEach | e) Unit
beforeEach = unsafeForeignProcedure ["fn",""] "window.beforeEach(fn);"
Later in the code I have the following:
beforeEach $ do
replaceState {title = "wowzers!", url = "/foos"}
and get the following error
Cannot unify Prelude.Unit with Control.Monad.Eff.Eff u2518 u2517.
I've tried manipulating the type signatures in various ways to try and make it all line up, but I don't really understand what is going wrong. So its just guessing at this point.
My modified version of your code is at the end of this post, but I had to make a few modifications in order to make it compile:
I assume the intention was for your StateUpdater to have an effect on the browser history, so I changed its type to use the Eff monad with a new History effect type. This was the main problem, since your last line used replaceState whose result type is not in the Eff monad. This resulted in the type error you saw.
I moved some of the universally quantified type variables in your type synonyms into function types. I also removed your Data type synonym and moved the data content into a new data field in the State type.
This is important, because your previous Data type had no inhabitants. There is a common misconception (for reasons I do not understand) that forall a. { | a} is a type of records "where I don't care about the fields". That is incorrect - this type represents the type of records which contains all fields which could possibly exist, and such a type is clearly uninhabited. There is an important difference between forall a. {| a} -> r and (forall a. {| a}) -> r from the point of view of the caller.
In answer to your original question: "what is a row of effects, and why are they useful?" - rows were originally added to PureScript to deal with polymorphism on record types without having to resort to subtyping. Rows allow us to give polymorphic types to functions which use records, in such a way that we can capture "the rest of the record" as a concept in the type system.
Rows also turned out to be a useful concept when dealing with effects. Just like we don't care what the rest of a record is, we usually don't care what the rest of a set of effects looks like, so long as all effects get propagated correctly in the type system.
To give an example, there are two effects involved in my modified code: History, and your BeforeEach. The actions beforeEach and replaceState each only use one of these effects, but their return types are polymorphic. This allows the combination of the two functions in main to have both effects, and type correctly. main has type forall eff. Eff (history :: History, beforeEach :: BeforeEach | eff) {} which is the most general type, inferred by the type checker.
In short, rows in the effect system provide a neat way to handle the interleaving of various "native" effects, so that the developer does not have to worry about things like the order of effects or lifting computations à la mtl.
module Main where
import EasyFFI
import Control.Monad.Eff
type Title = String
type Url = String
type State d = { title :: Title, url :: Url, "data" :: { | d } }
type StateUpdater d = forall eff. { | d } -> Title -> Url -> Eff (history :: History | eff) {}
foreign import data History :: !
unwrapState :: forall eff d. StateUpdater d -> State d -> Eff (history :: History | eff) {}
unwrapState f s = f s."data" s.title s.url
replaceState' :: forall d. StateUpdater d
replaceState' = unsafeForeignProcedure ["d","title","url"] "History.replaceState(d,title,url)"
replaceState :: forall eff d. State d -> Eff (history :: History | eff) {}
replaceState = unwrapState replaceState'
foreign import data BeforeEach :: !
beforeEach :: forall e a. Eff e a -> Eff (beforeEach :: BeforeEach | e) {}
beforeEach = unsafeForeignProcedure ["fn",""] "window.beforeEach(fn);"
main = beforeEach $ do
replaceState { title: "wowzers!", url: "/foos", "data": {} }
Related
Can someone tell me what is the use case of purescript-variants or variants in general
The documentation is very well written but I can't find any real use case scenario for it. Can someone tell how we could use Variants in real world?
Variants are duals of records. While records are sort of extensible ad-hoc product types (consider data T = T Int String vs. type T = { i :: Int, s :: String }), variants can be seen as extensible ad-hoc sum types - e.g. data T = A Int | B String vs. Variant (a :: Int, b :: String)
For example, just as you can write a function that handles a partial record:
fullName :: forall r. { first :: String, last :: String | r } -> String
fullName r = r.first <> " " <> r.last
myFullName = fullName { first: "Fyodor", last: "Soikin", weight: "Too much" }
so too, you can write a function that handles a partial variant:
weight :: forall r. Variant (kilos :: Int, vague :: String | r) -> String
weight =
default "Unknown"
# on _kilos (\n -> show n <> " kg.")
# on _vague (\s -> "Kind of a " <> s)
myWeight = weight (inj _kilos 100) -- "100 kg."
alsoMyWeight = weight (inj _vague "buttload") -- "Kind of a buttload"
But these are, of course, toy examples. For a less toy example, I would imagine something that handles alternatives, but needs to be extensible. Perhaps something like a file parser:
data FileType a = Json | Xml
basicParser :: forall a. FileType a -> String -> Maybe a
basicParser t contents = case t of
Json -> parseJson contents
Xml -> parseXml contents
Say I'm ok using this parser in most case, but in some cases I'd also like to be able to parse YAML. What do I do? I can't "extend" the FileType sum type post-factum, the best I can do is aggregate it in a larger type:
data BetterFileType a = BasicType (FileType a) | Yaml
betterParser :: forall a. BetterFileType a -> String -> Maybe a
betterParser t contents = case t of
BasicType bt -> basicParser bt contents
Yaml -> parseYaml contents
And now whenever I call the "better parser", I have to wrap the file type awkwardly:
result = betterParser (BasicType Json) "[1,2,3]"
Worse: now every consumer has to know the hierarchy of BetterFileType -> FileType, they can't just say "json", they have to know to wrap it in BasicType. Awkward.
But if I used extensible variants for the file type, I could have flattened them nicely:
type FileType r = (json :: String, xml :: String | r)
basicParser :: forall a r. Variant (FileType r) -> Maybe a
basicParser = onMatch { json: parseJson, xml: parseXml } $ default Nothing
----
type BetterFileType r = (yaml :: String | FileType r)
betterParser :: forall a r. Variant (BetterFileType r) -> Maybe a
betterParser = onMatch { yaml: parseYaml } basicParser
Now I can use the naked variant names with either basicParser or betterParser, without knowing to wrap them or not:
r1 = betterParser $ inj _json "[1,2,3]"
r2 = betterParser $ inj _yaml "foo: [1,2,3]"
I'm new to F# so forgive me in advance if this is a stupid question or if the syntax may be a bit off. Hopefully it's possible to understand the gist of the question anyways.
What I'd like to achieve is the possibility to compose e.g. Result's (or an Either or something similar) having different error types (discriminated unions) without creating an explicit discriminated union that includes the union of the two other discriminated unions.
Let me present an example.
Let's say I have a type Person defined like this:
type Person =
{ Name: string
Email: string }
Imagine that you have a function that validates the name:
type NameValidationError =
| NameTooLong
| NameTooShort
let validateName person : Result<Person, NameValidationError>
and another that validates an email address:
type EmailValidationError =
| EmailTooLong
| EmailTooShort
let validateEmail person : Result<Person, EmailValidationError>
Now I want to compose validateName and validateEmail, but the problem is that the error type in the Result has different types. What I'd like to achieve is a function (or operator) that allows me to do something like this:
let validatedPerson = person |> validateName |>>> validateEmail
(|>>> is the "magic operator")
By using |>>> the error type of validatedPerson would be a union of NameValidationError and EmailValidationError:
Result<Person, NameValidationError | EmailValidationError>
Just to make it clear, it should be possible to an use arbitrary number of functions in the composition chain, i.e.:
let validatedPerson : Result<Person, NameValidationError | EmailValidationError | XValidationError | YValidationError> =
person |> validateName |>>> validateEmail |>>> validateX |>>> validateY
In languages like ReasonML you can use something called polymorphic variants but this is not available in F# as afaict.
Would it be possible to somehow mimic polymorphic variants using generics with union types (or any other technique)?! Or is this impossible?
There's some interesting proposals for erased type unions, allowing for Typescript-style anonymous union constraints.
type Goose = Goose of int
type Cardinal = Cardinal of int
type Mallard = Mallard of int
// a type abbreviation for an erased anonymous union
type Bird = (Goose | Cardinal | Mallard)
The magic operator which would give you a NameValidationError | EmailValidationError would have its type exist only at compile-time. It would be erased to object at runtime.
But it's still on the anvil, so maybe we can still have some readable code by doing the erasing ourselves?
The composition operator could 'erase' (box, really) the result error type:
let (|>>) input validate =
match input with
| Ok(v) -> validate v |> Result.mapError(box)
| Error(e) -> Error(box e)
and we can have a partial active pattern to make type-matching DU cases palatable.
let (|ValidationError|_|) kind = function
| Error(err) when Object.Equals(kind, err) -> Some ()
| _ -> None
Example (with super biased validations):
let person = { Name = "Bob"; Email = "bob#email.com "}
let validateName person = Result.Ok(person)
let validateEmail person = Result.Ok(person)
let validateVibe person = Result.Error(NameTooShort)
let result = person |> validateName |>> validateVibe |>> validateEmail
match result with
| ValidationError NameTooShort -> printfn "Why is your name too short"
| ValidationError EmailTooLong -> printfn "That was a long address"
| _ -> ()
This will shunt on validateVibe
This is probably more verbose than you would like but it does allow you to put things into a DU without explicitly defining it.
F# has Choice types which are defined like this:
type Choice<'T1,'T2> =
| Choice1Of2 of 'T1
| Choice2Of2 of 'T2
type Choice<'T1,'T2,'T3> =
| Choice1Of3 of 'T1
| Choice2Of3 of 'T2
| Choice3Of3 of 'T3
// Going up to ChoiceXOf7
With your existing functions you would use them like this:
// This function returns Result<Person,Choice<NameValidationError,EmailValidationError>>
let validatePerson person =
validateName person
|> Result.mapError Choice1Of2
|> Result.bind (validateEmail >> Result.mapError Choice2Of2)
This is how you would consume the result:
let displayValidationError person =
match person with
| Ok p -> None
| Error (Choice1Of2 NameTooLong) -> Some "Name too long"
| Error (Choice2Of2 EmailTooLong) -> Some "Email too long"
// etc.
If you want to add a third validation into validatePerson you'll need to switch to Choice<_,_,_> DU cases, e.g. Choice1Of3 and so on.
In a learning environment, what are my options to provide type signatures for functions?
Standard ML doesn't have top-level type signatures like Haskell. Here are the alternatives I have considered:
Module signatures, which require either a separate signature file, or the type signature being defined in a separate block inside the same file as the module itself. This requires the use of modules, and in any production system that would be a sane choice.
Modules may seem a little verbose in a stub file when the alternative is a single function definition. They both introduce the concept of modules, perhaps a bit early,
Using val and val rec I can have the complete type signature in one line:
val incr : int -> int =
fn i => i + 1
val rec map : ('a -> 'b) -> 'a list -> 'b list =
fn f => fn xs => case xs of
[] => []
| x::ys => f x :: map f ys
Can I have this and also use fun?
If this is possible, I can't seem to get the syntax right.
Currently the solution is to embed the argument types and the result type as such:
fun map (f : 'a -> 'b) (xs : 'a list) : 'b list =
raise Fail "'map' is not implemented"
But I have experienced that this syntax gives the novice ML programmer the impression that the solution either cannot or should not be updated to the model solution:
fun map f [] = []
| map f (x::xs) = f x :: map f xs
It seems then that the type signatures, which are supposed to aid the student, prevents them from pattern matching. I cannot say if this is because they think that the type signatures cannot be removed or if they should not be removed. It is, of course, a matter of style whether they should (and where), but the student should be enabled to explore a style of type inference.
By using a let or local bound function, and shadowing
you can declare the function, and then assign it to a value.
using local for this is more convenient, since it has the form:
local decl in decl end, rather than let decl in expr end,
meaning let's expr, wants a top-level argument f
val map = fn f => let fun map = ... in map end
I don't believe people generally use local, anymore primarily because modules can do anything that local can, and more, but perhaps it is worth considering it as an anonymous module, when you do not want to explain modules yet.
local
fun map (f : 'a -> 'b) (x::rest : 'a list) : 'b list
= f x :: map f rest
| map _ ([]) = []
in
val (map : ('a -> 'b) -> 'a list -> 'b list) = map;
end
Then when it comes time to explain modules, you can declare the structure inside the local, around all of the declarations,
and then remove the local, and try to come up with a situation, where they have coded 2 functions, and it's more appropriate to replace 2 locals, with 1 structure.
local
structure X = struct
fun id x = x
end
in val id = X.id
end
perhaps starting them off with something like the following:
exception ReplaceSorryWithYourAnswer
fun sorry () = raise ReplaceSorryWithYourAnswer
local
(* Please fill in the _'s with the arguments
and the call to sorry() with your answer *)
fun map _ _ = sorry ()
in
val map : ('a -> 'b) -> ('a list) -> ('b list) = map
end
I am attempting to change the value of a key in a map I made in OCaml:
module TestMap = Map.Make(String);;
let m = TestMap.empty;;
let m = TestMap.add "Chris" 1 m ;;
let m = TestMap.add "Julie" 4 m;;
This compiles file, but when I try to update the value at key Julie with:
let m = TestMap.update "Julie" 10 m;;
I get an error from the compiler:
Error: This expression has type int but an expression was expected of type
'a option -> 'a option
I'm guessing that I'm maybe using the function incorrectly. I'm finding the documentation for Map.update pretty hard to understand:
val update : key -> ('a option -> 'a option) -> 'a t -> 'a t
Is my syntax or are my arguments incorrect?
The update function works in a way different from what you think
key -> ('a option -> 'a option) -> 'a t -> 'a t
You see that second argument is a function which takes an 'a option and returns an 'a option so you don't directly update with a new value but rather pass a function which returns the new value, according to the previous one, eg:
let m = TestMap.update "Julie" (fun _ -> Some 10) m;;
This because, as documentation states, the passed 'a option tells you if there was a mapping for the key and the returned 'a option allows you to change it or even remove it (through None).
If you need just to update a mapping you can use Map.add again, there's no need to use more advanced Map.update.
I wish to implement a stack with value restriction.
What I want is that the pop and push are always talking about exactly the same type all the time.
Here is my sig.
module type MyStackSig =
sig
type 'a stack
exception EmptyStack
val create : unit -> 'a stack
val push : 'a stack -> 'a -> unit
val pop : 'a stack -> 'a
val is_empty : 'a stack -> bool
val size : 'a stack -> int
end;;
Is this sig enough for the value restriction?
I mean will push and pop be talking about the same type all the time?
Your signature looks correct. The types tell that push and pop handle the same content type when they take the same stack. The type parameter 'a of stack ensures it.
let st = create () in
push st 1;
print_string (pop st)
is ill typed, since the st cannot have a polymorphic type due to value restriction, however, it is used for more than one type: int stack and string stack: "they do not talk about the same type" for one stack st, whose parameter type is value-restricted.
On the other hand, the following is well-typed:
let st1 = create () in
let st2 = create () in
push st1 1;
print_string (pop st2) (* it should raise EmptyStack, but do not care it here *)
Here, push and pop talk about different types, but of different stacks. So, no problem.
(Relaxed) value restriction is not something you can force. You are forced to live with it. It is a limitation of the type system for typing of side effects in OCaml.