Почему это функциональное приложение генерирует ошибку времени выполнения в purescript? - PullRequest
1 голос
/ 18 апреля 2019

У меня есть следующие фрагменты 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, но это проблема. Когда я вручную удаляю () в сгенерированном источнике, он работает как положено.

Обновление: Добавлен код для воспроизведения этого на этой ветви . После обычных формальностей, npm run testbrowser и откройте dist/index.html в браузере.

1 Ответ

2 голосов
/ 18 апреля 2019

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 модуле вы определяете 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 и друзья . Это простые оболочки, которые «переводят» функции в стиле JavaScript (то есть принимают все параметры одновременно) в эффективные карри-функции в стиле PureScript (то есть принимают параметры по одному и возвращают эффекты).

Вы можете объявить свою сторону JS как обычную функцию JS, затем импортировать ее в PureScript как EffectFnX и вызывать ее, используя 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 и друзья - то же самое, но для чистых (неэффективных) функций.

...