PureScript FFI to mocha

ffi, mocha.js, monads, purescript

Solution

The foreign function interface of PureScript is actually very simple. For example suppose you have the following JavaScript function:

function si(p) {
    return function (r) {
        return function (t) {
            return p * r * t / 100;
        };
    };
}

You could import it as follows:

foreign import si :: Number -> Number -> Number -> Number

You could also inline the function as follows:

foreign import si
    "function si(p) {\
    \    return function (r) {\
    \        return function (t) {\
    \            return p * r * t / 100;\
    \        };\
    \    };\
    \}" :: Number -> Number -> Number -> Number

For side effects PureScript doesn't use the `IO` monad. Instead it makes use of the `Eff` monad.

From what I understand the `Eff` monad is the same as the `IO` monad with an extra type parameter: a row of effects.

For example, in Haskell the `print` function has the following type:

print :: Show a => a -> IO ()

In PureScript the `print` function has the following type:

print :: Show a => a -> Eff (trace :: Trace | r) Unit

So what do we understand from this?

- `IO` is similar to `Eff e` where `e` is a row of effects.

- `Unit` is similar to `()`.

- The `print` function has the `trace` effect which is of the type `Trace`.

- In addition, the `print` function can be combined with an other effect. Row polymorphism. This means that it is composable.

An `Eff` value by itself is called an action. For example `print "Hello World!"` which is of the type `Eff (trace :: Trace | r) Unit` is an action.

An `Eff` value which is an argument to a function is called a handler. It can be thought of as a higher-order effectful function with no parameters.

An `Eff` value with no side-effects is known as a pure value:

type Pure a = forall e. Eff e a
runPure :: Pure a -> a

Since the row of effects (i.e. `e`) is polymorphic (or in other words empty, a black hole), PureScript assumes that the function has no side-effects. However it also means that it can be composed with other effectful functions.

The `Eff` monad is a contract between the programmer and the compiler in which the programmer promises the compiler that the given `Eff` value will only have the stated row of effects and no more.

Coming to your `describe` function:

Describe is a function that takes nothing and returns IO, or Eff or something that means (side-effect happened no value returned).

Actually this is wrong. Your `describe` function does take a function as an argument:

describe(function(){
  //do stuff
});

In addition the function that it takes has no arguments, which means that it is an effectful function. Hence it must be of the type `Eff e a` where `e` and `a` can be any row of effects and any return value respectively.

Thus your describe function must be of the type:

describe :: Eff e a -> Eff (describe :: Describe | e) {}

In Haskell it would be written as follows:

describe :: IO a -> IO ()

PureScript is just more explicit than Haskell. Anyway, `Describe` is a new effect type that you create which distinguishes it from other effect types such as `Trace`:

foreign import data Describe :: !

You would then import `describe` as follows:

foreign import describe
    "function describe(f) {\
    \    return function () {\
    \        window.describe(f);\
    \    };\
    \}" :: forall e a. Eff e a -> Eff (describe :: Describe | e) {}

Finally you can use it as follows:

main = do
    describe $ print "Hello World!"

The entire code is as follows:

module Main where

import Control.Monad.Eff
import Debug.Trace

foreign import data Describe :: !

foreign import describe
    "function describe(f) {\
    \    return function () {\
    \        window.describe(f);\
    \    };\
    \}" :: forall e a. Eff e a -> Eff (describe :: Describe | e) {}

main = do
    describe $ print "Hello World!"

It would produce the following JavaScript:

var PS = PS || {};

PS.Main = (function () {
    "use strict";

    var Prelude = PS.Prelude;
    var Debug_Trace = PS.Debug_Trace;

    function describe(f) {
        return function () {
            window.describe(f);
        };
    }

    var print = Debug_Trace.print(Prelude.showString({})); 

    var main = describe(print("Hello World!"));

    return {
        main: main, 
        describe: describe
    };
}());

Hope that helps.

Problem

I am trying to write mocha bindings into PureScript and am completely baffled by `Control.Monad.Eff` ``` describe(function(){ //do stuff }); ``` Describe is a function that takes nothing and returns IO, or Eff or something that means (side-effect happened no value returned). My attempts so far ``` foreign import describe "function describe(n){ \ \ return function(){ \ \ window.describe(n); \ \ }; \ \}" :: forall eff a. Eff eff a -> Eff eff foreign import describe "describe" :: forall eff a. Eff eff a -> Eff eff foreign import describe "describe" :: Eff -> Eff foreign import describe "describe" :: forall eff a. (a -> Eff eff) -> Eff eff ``` Clearly missing something here. Please help.

Original source