Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I write a purescript effect handler that subracts an effect from the set

Tags:

purescript

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.

Update

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
like image 809
mjkaye Avatar asked May 27 '15 16:05

mjkaye


1 Answers

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:

  • # -> It's a row ...
  • ! -> ... made up out of effects

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" )
like image 150
Christoph Hegemann Avatar answered Dec 31 '22 19:12

Christoph Hegemann