Effect Unification Issue

44 views
Skip to first unread message

frabbit

unread,
Jan 5, 2018, 10:29:05 AM1/5/18
to purescript
Hi Guys, 

i'm quite new to purescript, i ran into the following problem:

unifyWithRef :: forall eff a. Eff eff a -> Eff ( ref :: REF | eff ) a
unifyWithRef = unsafeCoerceEff

foo ::
forall eff .
Eff ( console :: CONSOLE | eff ) Unit
-> Eff ( console :: CONSOLE, ref :: REF | eff) Unit
foo param = f
where
f :: Eff (ref :: REF, console :: CONSOLE | eff) Unit
f = do
consoleAndRef
-- param
unifyWithRef param
pure unit


The following code works, but only with unifyWithRef (unsafeCoerceEff). If i just use param directly it fails to unify 

Eff ( console :: CONSOLE | eff ) Unit with Eff ( console :: CONSOLE, ref :: REF | eff) Unit

Can somebody explain why this happens and what's the best way to deal with it?

best,
frabbit

Nathan Faubion

unread,
Jan 5, 2018, 11:57:43 AM1/5/18
to purescript
One question would be, why do you want the input to unify with console, but not with ref?

You've declared your `param` as unifying with console and some tail `eff`, and you've declared your return type as unifying with console + ref with the same tail `eff`. So one has eff + console, and one has eff + console + ref, which means they can't possibly be the same set of rows, and thus can't unify. The answer is to use the same rows in the input as in the output.

foo ::
forall eff .
Eff ( console :: CONSOLE, ref :: REF | eff) Unit
-> Eff ( console :: CONSOLE, ref :: REF | eff) Unit

That is, my return effect must unify with console + ref, and if I'm going to take a parameter effect, it must also unify with console + ref if they are to be interleaved.

frabbit

unread,
Jan 5, 2018, 1:26:28 PM1/5/18
to purescript
Yeah, that totally makes sense, the problem is a little bit more complex, i'm trying to wrap my head around the react-native type signatures. Maybe this example is a little bit better:

foo1 ::
forall eff .
Eff ( console :: CONSOLE, state :: ReactState ( ) | eff ) Unit
-> Tuple
(Eff ( console :: CONSOLE, state :: ReactState ( ) | eff) Unit)
(Eff ( console :: CONSOLE, state :: ReactState ( read :: Read ) | eff) Unit)
foo1 param = Tuple param param

I want the input to unify with both types but i can't find a way to do this ( in my case i want to use the same effect inside of componentDidMount and render)

I could pass the same argument twice like this:

foo1 ::
forall eff.
Eff ( console :: CONSOLE, state :: ReactState ( ) | eff ) Unit
-> Eff ( console :: CONSOLE, state :: ReactState ( read :: Read ) | eff) Unit
-> Tuple
(Eff ( console :: CONSOLE, state :: ReactState ( ) | eff) Unit)
(Eff ( console :: CONSOLE, state :: ReactState ( read :: Read ) | eff) Unit)
foo1 param param1 = Tuple param param1

but that doesn't seem like a good idea.

Nathan Faubion

unread,
Jan 6, 2018, 12:19:07 PM1/6/18
to purescript
For this case, you just want the type to be polymorphic in it's state capabilities.

foo1 :: forall eff rs. Eff (console :: CONSOLE, state :: ReactState :: (read :: Read | rs) | eff) Unit

This effect can then be used in any context that supports state reading, which may include a context that supports writing as well. It's the same thing you'd do with eff rows.

Nathan Faubion

unread,
Jan 6, 2018, 12:19:46 PM1/6/18
to purescript
Corrected type:

foo1 :: forall eff rs. Eff (console :: CONSOLE, state :: ReactState (read :: Read | rs) | eff) Unit


frabbit

unread,
Jan 8, 2018, 4:32:28 AM1/8/18
to purescript
thx, yeah I already tried that, i guess my problem is that the receiver functions (componentDidMount and render) are not polymorphic in those capabilities:

type ComponentDidMount props state eff =
  ReactThis props state ->
  Eff
    ( props :: ReactProps
    , state :: ReactState (read::Read, write::Write)
    , refs :: ReactRefs (read::Read)
    | eff
    ) Unit

and

type Render props state render eff =
  ReactThis props state ->
  Eff
    ( props :: ReactProps
    , refs :: ReactRefs ()
    , state :: ReactState (read::Read)
    | eff
    ) render

which makes sense because if you only want to allow read, you don't want something that allow read + something else.

Nathan Faubion

unread,
Jan 9, 2018, 12:25:10 PM1/9/18
to purescript
Do you have an example where this failed? I have to do this in my own codebase where I have a function that needs to be run in both `willMount` and `didUnmount`.
Reply all
Reply to author
Forward
0 new messages