PureScript FFI для мокко

Я пытаюсь записать привязки mocha в PureScript и полностью озадачен Control.Monad.Eff

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

Опишите - это функция, которая ничего не принимает и возвращает IO, или Eff, или что-то, что означает (побочный эффект не возвращал значения).


Мои попытки пока

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

Очевидно, что здесь что-то не хватает. Пожалуйста, помогите.

Ответ 1

Внешний интерфейс PureScript на самом деле очень прост. Например, предположим, что у вас есть следующая функция JavaScript:

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

Вы можете импортировать его следующим образом:

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

Вы также можете включить функцию следующим образом:

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

Для побочных эффектов PureScript не использует монаду IO. Вместо этого он использует монаду Eff.

Из того, что я понимаю, монада Eff такая же, как монада IO с дополнительным параметром типа: ряд эффектов.

Например, в Haskell функция print имеет следующий тип:

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

В PureScript функция print имеет следующий тип:

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

Итак, что мы понимаем из этого?

  • IO похож на Eff e, где e представляет собой ряд эффектов.
  • Unit похож на ().
  • Функция print имеет эффект trace, который имеет тип trace.
  • Кроме того, функция print может быть объединена с другим эффектом. Полиморфизм строк. Это означает, что он является составным.

Значение An Eff само по себе называется действием. Например, print "Hello World!", который имеет тип Eff (trace :: Trace | r) Unit, является действием.

Значение an Eff, являющееся аргументом функции, называется обработчиком. Его можно рассматривать как эффективную функцию более высокого порядка без параметров.

Значение Eff без побочных эффектов известно как чистое значение:

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

Поскольку строка эффектов (т.е. e) является полиморфной (или, другими словами, пустой, черной дырой), PureScript предполагает, что функция не имеет побочных эффектов. Однако это также означает, что он может быть скомбинирован с другими эффективными функциями.

Монада Eff - это контракт между программистом и компилятором, в котором программист promises компилятор, что данное значение Eff будет иметь только указанную строку эффектов и не более.


Переход к вашей функции describe:

Опишите - это функция, которая ничего не принимает и возвращает IO, или Eff, или что-то, что означает (побочный эффект не возвращал значения).

На самом деле это неправильно. Ваша функция describe принимает функцию в качестве аргумента:

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

Кроме того, функция, которую он принимает, не имеет аргументов, что означает, что она является эффективной. Следовательно, он должен быть типа Eff e a, где e и a может быть любой строкой эффектов и любым возвращаемым значением соответственно.

Таким образом, ваша описательная функция должна быть типа:

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

В Haskell это будет записано следующим образом:

describe :: IO a -> IO ()

PureScript является более явным, чем Haskell. В любом случае, describe - это новый тип эффекта, который вы создаете, который отличает его от других типов эффектов, таких как trace:

foreign import data Describe :: !

Затем вы импортируете 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!"

Весь код выглядит следующим образом:

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!"

Он создаст следующий 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
    };
}());

Надеюсь, что это поможет.