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.