PureScript by Example, in the section The Eff Monad -> Handlers and Actions states that "[an effect] handler usually subtracts effects from the set". However, the examples are rather opaque and I can't work out how to write my own handler to achieve this.
Specifically, I'm working with purescript-aff and purescript-affjax. I'd like to use runAff (from purescript-aff) in combination with get (from purescript-affjax).
The problem is that get
uses the Affjax monad, whereas runAff
expects something using the Aff monad.
Affjax is defined as:
type Affjax e a = Aff (ajax :: AJAX | e) (AffjaxResponse a)
Aff is defined as:
foreign import data Aff :: # ! -> * -> *
I, therefore, want to write a function that has the following type:
Affjax e a -> Aff e a
This seems to me to require a handler that subtracts the ajax
part of the effect set. How would one code such a handler?
Attempting to pattern-match, as below, of course results in the error unexpected |
.
handleAffjax :: Affjax e a -> Aff e a
handleAffjax (Aff ( | eff1 ) resp1) = Aff eff1 resp1
Thanks all.
Inspired by @christoph-hegemann's answer below, I was able to track down most of the issues with my code.
I think the answer to the question in the title is that one does not subtract an effect from the set, and that description is a little confusing. The effect remains after you've handled it.
The intuition I was missing is that I have to add the expected effects to the calling function type. The lack of a type declaration on main
hid that to some extent. I worked it out when I turned Christoph's example into the following compiling example:
module Main where
import Debug.Trace
import Network.HTTP.Affjax
import Control.Monad.Aff
import Control.Monad.Eff
import Control.Monad.Eff.Exception
initialUrl :: URL
initialUrl = "http://127.0.0.1:8000/api/v1/navitem/2/"
runGet :: forall e. Eff (ajax :: AJAX, trace :: Trace | e) Unit
runGet = runAff errorHandler successHandler (get initialUrl)
errorHandler :: forall e. Error -> Eff (trace :: Trace | e) Unit
errorHandler err = print err
successHandler :: forall e. AffjaxResponse String -> Eff (trace :: Trace | e) Unit
successHandler res = print res.response
main = runGet
I'll take it step by step ^^
The problem is that get uses the Affjax monad, whereas runAff expects something using the Aff monad.
Affjax is defined as:
type Affjax e a = Aff (ajax :: AJAX | e) (AffjaxResponse a)
Affjax is a type synonym. This means that Affjax is not it's own Monad but instead a special case of the Aff Monad. Let's take a look at that next:
Aff is defined as:
foreign import data Aff :: # ! -> * -> *
So Aff is a type constructor that takes 2 Types as Arguments to produce a new type. The kind of the first argument is # ! which tells us that:
So when you look at the definition of the Affjax type synonym you can see that the first argument to the Aff type constructor is a row of effects which contains the ajax :: Ajax
effect but is open for extension | e
.
The second argument to Aff is just a type which in the case of Affjax an AffjaxResponse parameterised by the type variable a.
Looking at Pursuit reveals that AffjaxResponse is merely a type synonym for a record so you can access it's members by using the dot operator.
I've put together a minimal compiling example which should get you started:
module Main where
import Debug.Trace
import Network.HTTP.Affjax
import Control.Monad.Aff
import Control.Monad.Eff
import Control.Monad.Eff.Exception
errorHandler :: forall e. Error -> Eff (trace :: Trace | e) Unit
errorHandler err = print err
successHandler :: forall e. AffjaxResponse String -> Eff (trace :: Trace | e) Unit
successHandler res = print res.response
main = runAff errorHandler successHandler (get "http://www.myApi.com/api" )
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With