为什么此函数应用程序会在purescript中生成运行时错误?

时间:2019-04-18 16:15:46

标签: purescript

我有以下PureScript片段;注意parseXMLFromString被部分应用:

parseXMLFromString ∷ String → DOMParser → Effect Document
parseXMLFromString s d =
  parseFromString "application/xml" s d

parseNoteDoc :: DOMParser -> Effect Document
parseNoteDoc = parseXMLFromString TD.noteXml

note <- parseNoteDoc domParser

将生成以下代码:

// Generated by purs version 0.12.4
"use strict";
var Effect_Console = require("../Effect.Console/index.js");
var Test_Data = require("../Test.Data/index.js");
var Web_DOM_DOMParser = require("../Web.DOM.DOMParser/index.js");
var parseNoteDoc = Web_DOM_DOMParser.parseXMLFromString(Test_Data.noteXml);
var main = function __do() {
    var v = Web_DOM_DOMParser.makeDOMParser();
    var v1 = parseNoteDoc(v)();
    return Effect_Console.log("TODO: You should add some tests.")();
};
module.exports = {
    parseNoteDoc: parseNoteDoc,
    main: main
};

var v1 = parseNoteDoc(v)();行给出了错误TypeError: parseNoteDoc(...) is not a function

我不确定()上多余的parseNoteDoc来自哪里,但这就是问题所在。当我在生成的源中手动删除()时,它可以按预期工作。

更新:添加了用于在this branch上重现此代码的代码。完成通常的手续后,npm run testbrowser并在浏览器中打开dist/index.html

1 个答案:

答案 0 :(得分:2)

TL; DR:您的FFI代码不正确,您需要添加额外的function()


详细说明

多余的空括号来自Effect

这是在PureScript中建模有效计算的方式:有效计算不是值,而是值的“承诺”,您可以评估并获得结果。值的“承诺”可以建模为返回值的函数,而这正是在PureScript中建模的方式。

例如,此:

a :: Effect Unit

编译为JavaScript的方式是:

function a() { return {}; }

与此类似,

f :: String -> Effect Unit

编译为JavaScript的方式是:

function f(s) { return function() { return {}; } }

因此,它将字符串作为参数,然后返回Effect Unit,它本身就是JS中的无参数函数。

但是,在您的FFI module中,您将parseFromString定义为:

exports.parseFromString = function (documentType) {
  return function (sourceString) {
    return function (domParser) {
      return domParser.parseFromString(sourceString, documentType);
    };
  };
};

parseFromString :: String -> String -> DOMParser -> Document等效-即,它采用三个参数,一个接一个,然后返回已解析的文档。

但是在PureScript方面,您将其定义为parseFromString :: String -> String -> DOMParser -> Effect Document-这意味着它应该一个接一个地接受三个参数,然后返回一个Effect Document-如上所述,它应该是,无参数函数。正是这种额外的无参数调用在尝试评估Effect Unit时失败,而实际上它根本不是Effect,而是Document

因此,为了修复您的FFI,您只需要插入一个额外的无参数函数即可对返回的Effect进行建模:

exports.parseFromString = function (documentType) {
  return function (sourceString) {
    return function (domParser) {
      return function() {
        return domParser.parseFromString(sourceString, documentType);
      }
    };
  };
};

(有趣的是,makeDOMParser :: Effect DOMParser在FFI模块中已正确建模为无参数函数)


但是有更好的方法

这些JS中嵌套函数的金字塔看起来确实很难看,您必须同意。因此,为此而准备的应用程序-EffectFn1, runEffectFn1, and friends也就不足为奇了。这些是简单的包装器,可将JavaScript样式的函数(即一次获取所有参数)“转换”为PureScript样式的有效函数(即一个一地获取参数并返回效果)。

您可以将JS端声明为普通的JS函数,然后将其作为EffectFnX导入PureScript,并在需要时使用runEffectFnX进行调用:

// JavaScript:
exports.parseFromString = function (documentType, sourceString, domParser) {
  return domParser.parseFromString(sourceString, documentType);
};

-- PureScript:
foreign import parseFromString ∷ EffectFn3 String String DOMParser Document

parseHTMLFromString ∷ String → DOMParser → Effect Document
parseHTMLFromString s d =
  runEffectFn3 parseFromString "text/html" s d

P.S。购买EffectFn1的人也喜欢Fn1 and friends-一样,只是功能纯(无效)。