12

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.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
Fresheyeball
  • 29,567
  • 20
  • 102
  • 164
  • 1
    Is this question about Haskell at all (apart from Purescript being written in it)? It'd probably be better to avoid the `haskell` tag if not, as it's a bit confusing. – Ganesh Sittampalam Jul 14 '14 at 13:17

1 Answers1

14

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?

  1. IO is similar to Eff e where e is a row of effects.
  2. Unit is similar to ().
  3. The print function has the trace effect which is of the type Trace.
  4. 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.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • 1
    That was extremely helpful. But I am still not quite understanding a few things. I understand the Haskell version, but the purescript still confuses me. Why is `forall` necessary? What is the `| e)` mean in the record? Why is the record followed with `{}`? – Fresheyeball Jul 14 '14 at 13:17
  • Your response is amazing, I'm accepting it regardless. Thank you. – Fresheyeball Jul 14 '14 at 13:21
  • 3
    The `forall` in Haskell is implicit, which means that you don't need to type it. Hence in Haskell if you type `foldl :: (a -> b -> a) -> a -> [b] -> a` the compiler assumes that it means `foldl :: forall a b. (a -> b -> a) -> a -> [b] -> a`. However in PureScript the `forall` is explicit which means that you always have to type it or else the compiler will generate an error. It's ugly, but it's still ok. – Aadit M Shah Jul 14 '14 at 13:28
  • 4
    The type `forall e. { a :: A, b :: B | e }` means "match an object with the properties `a :: A` and `b :: B`, __and any other properties__". This is necessary because an object may have additional properties. If we only wrote `{ a :: A, b :: B }` then the compiler would assume that the object only had the properties `a :: A` and `b :: B` and nothing else. This means that if the object had additional properties then the compiler would throw an error. Adding the extra `| e` at the end tells the compiler that it's okay for the object to have additional properties. Also enables merging of objects. – Aadit M Shah Jul 14 '14 at 13:32
  • 3
    The kind signature of the `Eff` monad is `Eff :: # ! -> * -> *`. This means that the first parameter is a __row__ (denoted by `#`) of __effects__ (denoted by `!`). The second parameter is any pure (i.e. non-effectful) value (denoted by `*`). The result is also a pure value. Hence the `Eff` monad requires two parameters: a row of effect types and a return type. The type of your `describe` function is `Eff e a -> Eff (describe :: Describe | e) {}`. This means that the argument can have any number of effects and return any value. The return type adds a new effect and returns an empty object value – Aadit M Shah Jul 14 '14 at 13:39
  • You rock. This all makes sense now. Can wait to get home and play with this. Hopefully I can publish `mocha` `chai` and `sinon` bindings. – Fresheyeball Jul 14 '14 at 16:24
  • I would only make the following small modification: since the Mocha docs seem to indicate that calls to `describe` can be nested, you might want the following type signature: `forall e a. Eff (describe :: Describe | e) a -> Eff (describe :: Describe | e) Unit` – Phil Freeman Jul 14 '14 at 17:44
  • @PhilFreeman That would be problematic because then the compiler would expect every function passed to `describe` to have the `Describe` effect. This would mean that expressions like `describe $ print "Hello World!"` would be illegal because `print "Hello World!"` does not have the `Describe` effect. It only has the `Trace` effect. On the other hand `Eff e a` is generic. The polymorphic row of effects `e` means that it could have any effects including the `Describe` effect. The return value is `e` plus the `Describe` effect. If `Describe` is already in `e` then it wouldn't make any difference. – Aadit M Shah Jul 15 '14 at 03:12
  • @AaditMShah Not true. `print "Hello World"` has a polymorphic type, which can be specialised to `Eff (trace :: Trace, describe :: Describe) Unit` – Phil Freeman Jul 15 '14 at 17:50
  • @PhilFreeman I stand corrected. I tried compiling `describe $ describe $ print "Hello World!"` using both your method and my method at http://try.purescript.org and it works either way. So it doesn't really make a difference whether we use `Eff e a` or `Eff (describe :: Describe | e) a`. – Aadit M Shah Jul 16 '14 at 06:15
  • I like the way this answer embeds Javascript right into the Purescript file but I don't see that documented anywhere and I couldn't get it to work. I'm using Purescript 11, and it's four years later 2018. Is this syntax no longer supported? – Winston Mar 10 '18 at 00:03
  • @Winston I wouldn't know because I don't use PureScript. However, you can ask Phil Freeman (read the comments above). He's the original author of PureScript. – Aadit M Shah Mar 10 '18 at 05:27