将 PureScript 类型公开给 JavaScript

Exposing PureScript types to JavaScript

我可以在 JS 中使用 PureScript 类型吗?例如:

type SomeType = /*type declaration*/

func :: SomeType
func = /*type construction*/

然后在 JS 中做这样的事情:

let a = module.func()
a instanceof module.SomeType === true

如果没有,是否有任何我可以实现它的 Haskellish 语言(Elm、GHCJS、Idris?)?

一般来说答案是"no"。运行时表示实际上取决于类型到底是什么。

对于 data 声明,每个构造函数都将由 JS "class" 表示,如下所示:

-- PureScript:
data T = A Int | B

x :: T
x = A 42

// JS:
function A(value0) { this.value0 = value0; }
A.create = function(value0) { return new A(value0); }

function B() { }
B.value = new B();

var x = A.create(42);

所以你可以在技术上使用 instanceof 区分大小写,但不能确定类型本身。

另一方面,对于 newtype,类型包装器将被完全删除,因为这就是 newtype:

的全部意义
-- PureScript:
newtype T = T Int

x :: T
x = T 42

// JS:
var x = 42;

而对于类型同义词(用 type 关键字声明),没有明确的表示。也就是说,此类类型在运行时的表示方式与它们的右侧表示方式相同:

-- PureScript
type T = Int

x :: T
x = 42

// JS:
var x = 42;

这也适用于裸记录,因为它们也只是类型同义词:

-- PureScript
type R = { x :: Int, y :: String }

r :: R
r = { x: 42, y: "foo" }

// JS:
var r = { x: 42, y: "foo" }

所以底线是:没有好的方法来做到这一点。 PureScript 根本不对运行时表示做出任何承诺。它对程序行为做出了很好的承诺,但对如何在 JS(或您碰巧使用的任何其他后端)中实现它没有承诺。

这是另一个问题: 因为 PureScript 没有做出任何这样的承诺,依赖你(我)对当前实现的了解是一个非常糟糕的主意,因为下一个版本的编译器可能会完全改变它。 PureScript 可以做到这一点,因为它没有做出任何承诺。看看它是如何工作的?

例如,我知道管道中某处有一个拉取请求,可以将 data 表示切换为数组,如下所示:

-- PureScript:
data T = A Int | B

x, y :: T
x = A 42
y = B

// JS:
var x = ["A", 42]
var y = ["B"]

如果此拉取请求被接受,您的程序将被破坏。


总而言之,我还可以告诉您,无论是 Elm、Haskell 还是 Idris 都不会表现出您想要的 属性。运行时类型信息在 ML 世界中通常不是有价值的东西。人们可以争辩说它以某种方式有效地存在于 Idris 中,但不是以您期望的方式。

您可能想尝试一下 F#/Fable:它在这方面稍强一些,但仍然不如其原生 .NET 运行时。我不认为你可以突然从 JS 检查 F# 对象的类型,但我知道 Fable 确实支持从 F# 本身访问 RTTI(以额外的性能成本),所以你至少可以制作函数来做到这一点和将它们导出到 JS。

如果您可以详细说明您的实际问题是什么,那么我(或其他人)也许可以提出替代解决方案。