Module

Control.Monad.Eff.Uncurried

Package
purescript-effDEPRECATED
Repository
purescript/purescript-eff

This module defines types for effectful uncurried functions, as well as functions for converting back and forth between them.

Traditionally, it has been difficult to give a PureScript type to JavaScript functions such as this one:

function logMessage(level, message) {
  console.log(level + ": " + message);
}

In particular, note that logMessage performs effects immediately after receiving all of its parameters, so giving it the type Data.Function.Fn2 String String Unit, while convenient, would effectively be a lie.

Because there has been no way of giving such functions types, we generally resort to converting functions into the normal PureScript form (namely, a curried function returning an Eff action), and performing the marshalling in JavaScript, in the FFI module, like this:

-- In the PureScript file:
foreign import logMessage :: forall eff.
  String -> String -> Eff (console :: CONSOLE | eff) Unit
// In the FFI file:
exports.logMessage = function(level) {
  return function(message) {
    return function() {
      logMessage(level, message);
    };
  };
};

This method, unfortunately, turns out to be both tiresome and error-prone. This module offers an alternative solution. By providing you with:

  • the ability to give the real logMessage function a PureScript type, and
  • functions for converting between this form and the normal PureScript form,

the FFI boilerplate is no longer needed. The previous example becomes:

-- In the PureScript file:
foreign import logMessageImpl :: forall eff.
  EffFn2 (console :: CONSOLE | eff) String String Unit
// In the FFI file:
exports.logMessageImpl = logMessage

You can then use runEffFn2 to provide a nicer version:

logMessage :: forall eff.
  String -> String -> Eff (console :: CONSOLE | eff) Unit
logMessage = runEffFn2 logMessageImpl

(note that this has the same type as the original logMessage).

Effectively, we have reduced the risk of errors by moving as much code into PureScript as possible, so that we can leverage the type system. Hopefully, this is a little less tiresome too.

Here's a slightly more advanced example. Here, because we are using callbacks, we need to use mkEffFn{N} as well.

Suppose our logMessage changes so that it sometimes sends details of the message to some external server, and in those cases, we want the resulting HttpResponse (for whatever reason).

function logMessage(level, message, callback) {
  console.log(level + ": " + message);
  if (level > LogLevel.WARN) {
    LogAggregatorService.post("/logs", {
      level: level,
      message: message
    }, callback);
  } else {
    callback(null);
  }
}

The import then looks like this:

foreign import logMessageImpl :: forall eff.
 EffFn3 (http :: HTTP, console :: CONSOLE | eff)
        String
        String
        (EffFn1 (http :: HTTP, console :: CONSOLE | eff)
           (Nullable HttpResponse)
           Unit)
        Unit

And, as before, the FFI file is extremely simple:

exports.logMessageImpl = logMessage

Finally, we use runEffFn{N} and mkEffFn{N} for a more comfortable PureScript version:

logMessage :: forall eff.
  String ->
  String ->
  (Nullable HttpResponse -> Eff (http :: HTTP, console :: CONSOLE | eff) Unit) ->
  Eff (http :: HTTP, console :: CONSOLE | eff) Unit
logMessage level message callback =
  runEffFn3 logMessageImpl level message (mkEffFn1 callback)

The general naming scheme for functions and types in this module is as follows:

  • EffFn{N} means, a curried function which accepts N arguments and performs some effects. The first type argument is the row of effects, which works exactly the same way as in Eff. The last type argument is the return type. All other arguments are the actual function's arguments.
  • runEffFn{N} takes an EffFn of N arguments, and converts it into the normal PureScript form: a curried function which returns an Eff action.
  • mkEffFn{N} is the inverse of runEffFn{N}. It can be useful for callbacks.

#EffFn1 Source

data EffFn1 :: Row Effect -> Type -> Type -> Type

#EffFn2 Source

data EffFn2 :: Row Effect -> Type -> Type -> Type -> Type

#EffFn3 Source

data EffFn3 :: Row Effect -> Type -> Type -> Type -> Type -> Type

#EffFn4 Source

data EffFn4 :: Row Effect -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn5 Source

data EffFn5 :: Row Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn6 Source

data EffFn6 :: Row Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn7 Source

data EffFn7 :: Row Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn8 Source

data EffFn8 :: Row Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn9 Source

data EffFn9 :: Row Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn10 Source

data EffFn10 :: Row Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#mkEffFn1 Source

mkEffFn1 :: forall r a eff. (a -> Eff eff r) -> EffFn1 eff a r

#mkEffFn2 Source

mkEffFn2 :: forall r b a eff. (a -> b -> Eff eff r) -> EffFn2 eff a b r

#mkEffFn3 Source

mkEffFn3 :: forall r c b a eff. (a -> b -> c -> Eff eff r) -> EffFn3 eff a b c r

#mkEffFn4 Source

mkEffFn4 :: forall r d c b a eff. (a -> b -> c -> d -> Eff eff r) -> EffFn4 eff a b c d r

#mkEffFn5 Source

mkEffFn5 :: forall r e d c b a eff. (a -> b -> c -> d -> e -> Eff eff r) -> EffFn5 eff a b c d e r

#mkEffFn6 Source

mkEffFn6 :: forall r f e d c b a eff. (a -> b -> c -> d -> e -> f -> Eff eff r) -> EffFn6 eff a b c d e f r

#mkEffFn7 Source

mkEffFn7 :: forall r g f e d c b a eff. (a -> b -> c -> d -> e -> f -> g -> Eff eff r) -> EffFn7 eff a b c d e f g r

#mkEffFn8 Source

mkEffFn8 :: forall r h g f e d c b a eff. (a -> b -> c -> d -> e -> f -> g -> h -> Eff eff r) -> EffFn8 eff a b c d e f g h r

#mkEffFn9 Source

mkEffFn9 :: forall r i h g f e d c b a eff. (a -> b -> c -> d -> e -> f -> g -> h -> i -> Eff eff r) -> EffFn9 eff a b c d e f g h i r

#mkEffFn10 Source

mkEffFn10 :: forall r j i h g f e d c b a eff. (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> Eff eff r) -> EffFn10 eff a b c d e f g h i j r

#runEffFn1 Source

runEffFn1 :: forall r a eff. EffFn1 eff a r -> a -> Eff eff r

#runEffFn2 Source

runEffFn2 :: forall r b a eff. EffFn2 eff a b r -> a -> b -> Eff eff r

#runEffFn3 Source

runEffFn3 :: forall r c b a eff. EffFn3 eff a b c r -> a -> b -> c -> Eff eff r

#runEffFn4 Source

runEffFn4 :: forall r d c b a eff. EffFn4 eff a b c d r -> a -> b -> c -> d -> Eff eff r

#runEffFn5 Source

runEffFn5 :: forall r e d c b a eff. EffFn5 eff a b c d e r -> a -> b -> c -> d -> e -> Eff eff r

#runEffFn6 Source

runEffFn6 :: forall r f e d c b a eff. EffFn6 eff a b c d e f r -> a -> b -> c -> d -> e -> f -> Eff eff r

#runEffFn7 Source

runEffFn7 :: forall r g f e d c b a eff. EffFn7 eff a b c d e f g r -> a -> b -> c -> d -> e -> f -> g -> Eff eff r

#runEffFn8 Source

runEffFn8 :: forall r h g f e d c b a eff. EffFn8 eff a b c d e f g h r -> a -> b -> c -> d -> e -> f -> g -> h -> Eff eff r

#runEffFn9 Source

runEffFn9 :: forall r i h g f e d c b a eff. EffFn9 eff a b c d e f g h i r -> a -> b -> c -> d -> e -> f -> g -> h -> i -> Eff eff r

#runEffFn10 Source

runEffFn10 :: forall r j i h g f e d c b a eff. EffFn10 eff a b c d e f g h i j r -> a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> Eff eff r