Eff monad demands row with Debug.Trace.Trace
functional-programming, purescript
Solution
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 `lift`ing 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": {} }
Problem
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.