【问题标题】:PureScript FFI to mochaPureScript FFI 转 mocha
【发布时间】:2014-07-14 01:58:06
【问题描述】:

我正在尝试将 mocha 绑定写入 PureScript 并且对 Control.Monad.Eff 完全感到困惑

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

Describe 是一个不接受任何内容并返回 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

这里显然遗漏了一些东西。请帮忙。

【问题讨论】:

  • 这个问题是关于 Haskell 的吗(除了写在里面的 Purescript)?如果没有,最好避免使用haskell 标签,因为它有点令人困惑。

标签: mocha.js ffi purescript


【解决方案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 monad。相反,它使用了Eff monad。

据我了解,Eff monad 与带有额外类型参数的 IO monad 相同:一行效果。

例如,在 Haskell 中,print 函数具有以下类型:

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

在 PureScript 中,print 函数具有以下类型:

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

那么我们从中理解了什么?

  1. IO 类似于Eff e,其中e 是一排效果。
  2. Unit 类似于 ()
  3. print 函数具有trace 效果,它属于Trace 类型。
  4. 此外,print 函数可以与其他效果结合使用。行多态性。这意味着它是可组合的。

Eff 值本身称为操作。例如print "Hello World!" 类型为Eff (trace :: Trace | r) Unit 是一个动作。

作为函数参数的Eff 值称为处理程序。它可以被认为是一个没有参数的高阶有效函数。

没有副作用的Eff 值称为纯值:

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

由于效果行(即e)是多态的(或者换句话说是空的,一个黑洞),PureScript 假定该函数没有副作用。但这也意味着它可以与其他有效的功能组合在一起。

Eff monad 是程序员和编译器之间的契约,其中程序员向编译器承诺给定的 Eff 值将仅具有规定的一行效果,而不会更多。


来到你的describe函数:

Describe 是一个不接受任何内容并返回 IO 或 Eff 或其他含义的函数(副作用发生没有返回值)。

其实这是错误的。您的 describe 函数确实将函数作为参数:

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

此外,它接受的函数没有参数,这意味着它是一个有效的函数。因此,它必须是 Eff e a 类型,其中 ea 可以分别是任何效果行和任何返回值。

因此您的描述函数必须是以下类型:

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
    };
}());

希望对您有所帮助。

【讨论】:

  • 这非常有帮助。但是我仍然不太了解一些事情。我了解 Haskell 版本,但 purescript 仍然让我感到困惑。为什么需要forall?记录中的| e) 是什么意思?为什么记录后面是{}
  • 你的反应太棒了,无论如何我都会接受。谢谢。
  • Haskell 中的forall 是隐式的,这意味着您不需要键入它。因此,在 Haskell 中,如果您键入 foldl :: (a -> b -> a) -> a -> [b] -> a,编译器会假定它的意思是 foldl :: forall a b. (a -> b -> a) -> a -> [b] -> a。但是在 PureScript 中,forall 是显式的,这意味着您必须始终键入它,否则编译器将生成错误。很丑,但还可以。
  • forall e. { a :: A, b :: B | e } 类型的意思是“将对象与属性a :: Ab :: B和任何其他属性匹配”。这是必要的,因为对象可能具有其他属性。如果我们只写{ a :: A, b :: B },那么编译器会假设该对象只有属性a :: Ab :: B,没有别的。这意味着如果对象有额外的属性,那么编译器会抛出一个错误。在末尾添加额外的| e 告诉编译器对象可以具有其他属性。还可以合并对象。
  • Eff monad 的签名是Eff :: # ! -> * -> *。这意味着第一个参数是效果(用!表示)的(用#表示)。第二个参数是任何纯(即无效)值(由* 表示)。结果也是一个纯值。因此Eff monad 需要两个参数:一行效果类型和一个返回类型。 describe 函数的类型是 Eff e a -> Eff (describe :: Describe | e) {}。这意味着参数可以具有任意数量的效果并返回任意值。返回类型添加新效果并返回一个空对象值
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-26
  • 1970-01-01
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多