gpt4 book ai didi

purescript - purescript 中列表/数组中的类似记录类型

转载 作者:行者123 更新时间:2023-12-03 19:45:48 27 4
gpt4 key购买 nike

有没有办法做类似的事情

first = {x:0}
second = {x:1,y:1}
both = [first, second]

使得 both推断为 {x::Int | r}或类似的东西?

我尝试了几件事:
[{x:3}] :: Array(forall r. {x::Int|r})    -- nope

test = Nil :: List(forall r. {x::Int|r})
{x:1} : test -- nope

type X r = {x::Int | r}
test = Nil :: List(X) -- nope
test = Nil :: List(X())
{x:1} : test
{x:1, y:1} : test -- nope

我能想到的一切似乎都告诉我,不支持将这样的记录合并到一个集合中。有点像,函数可以是多态的,但列表不能。这是正确的解释吗?它让我想起了 F#“值限制”问题,虽然我认为这只是因为 CLR 限制而 JS 不应该有这个问题。但也许这无关紧要。

有没有办法声明列表/数组来支持这个?

最佳答案

您正在寻找的是“existential types ”,而 PureScript 只是不支持 Haskell 那样的语法级别。但你可以推出自己的:-)

一种方法是“数据抽象” - 即根据您要对其执行的操作对数据进行编码。例如,假设您想获得 x 的值。在某个时候离开他们。在这种情况下,请创建以下数组:

type RecordRep = Unit -> Int

toRecordRep :: forall r. { x :: Int | r } -> RecordRep
toRecordRep {x} _ = x

-- Construct the array using `toRecordRep`
test :: Array RecordRep
test = [ toRecordRep {x:1}, toRecordRep {x:1, y:1} ]

-- Later use the operation
allTheXs :: Array Int
allTheXs = test <#> \r -> r unit

如果你有多个这样的操作,你可以随时记录它们:
type RecordRep = 
{ getX :: Unit -> Int
, show :: Unit -> String
, toJavaScript :: Unit -> Foreign.Object
}

toRecordRep r =
{ getX: const r.x
, show: const $ show r.x
, toJavaScript: const $ unsafeCoerce r
}

(注意每个函数中的 Unit 参数 - 它们是为了懒惰而存在的,假设每个操作都可能很昂贵)

但如果你真的需要打字机 ,你可以做我所谓的“穷人的存在类型”。如果仔细观察,存在类型只不过是“延迟”类型检查 - 延迟到您需要查看类型的程度。在 ML 语言中延迟某些东西的机制是什么?没错——一个函数! :-)
 newtype RecordRep = RecordRep (forall a. (forall r. {x::Int|r} -> a) -> a)

toRecordRep :: forall r. {x::Int|r} -> RecordRep
toRecordRep r = RecordRep \f -> f r

test :: Array RecordRep
test = [toRecordRep {x:1}, toRecordRep {x:1, y:1}]

allTheXs = test <#> \(RecordRep r) -> r _.x

这种工作方式是 RecordRep包装一个函数,该函数接受另一个函数,该函数在 r 中是多态的- 也就是说,如果您正在查看 RecordRep ,你必须准备给它一个可以与任何 r 一起工作的函数。 . toRecordRep以这样一种方式包装记录,使其精确类型在外部不可见,但它将用于实例化您最终将提供的泛型函数。在我的例子中,这样的函数是 _.x .

但是请注意,这里存在问题:行 r当您开始使用数组元素时,实际上是未知的,因此您无法对它做任何事情。就像,根本。您所能做的就是获取 x字段,因为它的存在是硬编码在签名中的,但除了 x - 你只是不知道。这是设计使然:如果您想将任何东西放入数组中,您必须准备好从中取出任何东西。

现在,如果你确实想对这些值做一些事情,你必须通过约束 r 来解释这一点。 , 例如:
newtype RecordRep = RecordRep (forall a. (forall r. Show {x::Int|r} => {x::Int|r} -> a) -> a)

toRecordRep :: forall r. Show {x::Int|r} => {x::Int|r} -> RecordRep
toRecordRep r = RecordRep \f -> f r

test :: Array RecordRep
test = [toRecordRep {x:1}, toRecordRep {x:1, y:1}]

showAll = test <#> \(RecordRep r) -> r show

路过 show像这样的函数有效,因为我们限制了行 r以这样的方式 Show {x::Int|r}必须存在,因此,应用 show{x::Int|r}必须工作。根据需要重复您自己的类型类。

这是有趣的部分 : 因为类型类是作为函数字典实现的,所以上面描述的两个选项实际上是等效的——在这两种情况下,你最终都会传递一个函数字典,只有在第一种情况下它是显式的,但在第二种情况下编译器会这样做为你。

顺便说一句,这也是 Haskell 语言支持的工作方式。

关于purescript - purescript 中列表/数组中的类似记录类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53270182/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com