Module

Control.Monad.Eff.Uncurried

Package
purescript-eff
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.

#EffFn1Source

data EffFn1 :: # Effect -> Type -> Type -> Type

#EffFn2Source

data EffFn2 :: # Effect -> Type -> Type -> Type -> Type

#EffFn3Source

data EffFn3 :: # Effect -> Type -> Type -> Type -> Type -> Type

#EffFn4Source

data EffFn4 :: # Effect -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn5Source

data EffFn5 :: # Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn6Source

data EffFn6 :: # Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn7Source

data EffFn7 :: # Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn8Source

data EffFn8 :: # Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn9Source

data EffFn9 :: # Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#EffFn10Source

data EffFn10 :: # Effect -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type -> Type

#mkEffFn1Source

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

#mkEffFn2Source

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

#mkEffFn3Source

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

#mkEffFn4Source

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

#mkEffFn5Source

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

#mkEffFn6Source

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

#mkEffFn7Source

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

#mkEffFn8Source

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

#mkEffFn9Source

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

#mkEffFn10Source

mkEffFn10 :: forall 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) -> EffFn10 eff a b c d e f g h i j r

#runEffFn1Source

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

#runEffFn2Source

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

#runEffFn3Source

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

#runEffFn4Source

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

#runEffFn5Source

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

#runEffFn6Source

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

#runEffFn7Source

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

#runEffFn8Source

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

#runEffFn9Source

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

#runEffFn10Source

runEffFn10 :: forall eff a b c d e f g h i j r. 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