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

purescript中列表/数组中的类似记录类型,purescript,Purescript,有没有办法做类似的事情 first = {x:0} second = {x:1,y:1} both = [first, second] 这样,两个都被推断为{x::Int | r}或者类似的东西 我试过几件事: [{x:3}] :: Array(forall r. {x::Int|r}) -- nope test = Nil :: List(forall r. {x::Int|r}) {x:1} : test -- nope

有没有办法做类似的事情

first = {x:0}
second = {x:1,y:1}
both = [first, second]
这样,
两个
都被推断为
{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不应该有这个问题。但也许这是无关的


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

您要找的是“”,而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
(请注意每个函数中的
Unit
参数——它们是为惰性而存在的,假设每个操作都可能很昂贵)

但是如果你真的需要这种类型的机器,你可以做我称之为“穷人的生存类型”。如果仔细观察,存在类型只不过是“延迟”类型检查——延迟到需要查看类型的位置。在ML语言中,什么是延迟的机制?没错-一个函数!:-)

像这样传递
show
函数是有效的,因为我们约束行
r
的方式是
show{x::Int | r}
必须存在,因此,将
show
应用于
{x::Int | r}
必须有效。根据需要为您自己的类型类重复上述操作

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


顺便说一句,Haskell语言对此的支持也是如此。

根据@FyodorSoikin基于“存在类型”的答案,我们可以在
purescript中找到另一个解决方案。
最后,我们将能够构建一个记录的
数组
,它将“同构”到:

存在尾部。数组{x::Int | tail}
让我们从类型构造函数开始,它可以用来对行类型(种类类型<代码>#类型)进行存在量化。我们不能在这里使用
purescript Exists
中的
Exists
,因为purescript没有类型多态性,原始
Exists
是通过
类型
参数化的

newtype Exists f=Exists(对于所有a.f(a:#Type))
我们可以遵循和重新实现(
;-)定义,并构建一组工具来处理这些
存在的
值:

modulemain其中
进口序曲
导入不安全。强制(不安全强制)
导入数据.Newtype(类Newtype,展开)
newtype Exists f=Exists(对于所有a.f(a::#类型))
mkExists::对于所有f a。f a->存在f
mkExists r=Exists(对于所有a.f.a,未安全强制r::)
runExists::forall b f。(对于所有a.f a->b)->存在f->b
runExists g(Exists f)=g f
使用它们,我们能够构建一个
数组
记录
,带有“任意”尾部,但我们必须在以下情况之前将任何此类记录类型包装在
新类型中:

newtype rt=R{x::Int | t}
派生实例newtypeRec::Newtype(R t)_
现在我们可以使用
mkExists
构建
数组

arr :: Array (Exists R)
arr = [ mkExists (R { x: 8, y : "test"}), mkExists (R { x: 9, z: 10}) ]
x :: Array [ Int ]
x = map (runExists (unwrap >>> _.x)) arr
并使用
runExists
处理值:

arr :: Array (Exists R)
arr = [ mkExists (R { x: 8, y : "test"}), mkExists (R { x: 9, z: 10}) ]
x :: Array [ Int ]
x = map (runExists (unwrap >>> _.x)) arr

很有意思,你正好知道我在调查什么。如果类和函数集是等价的,那么是否需要类呢?i、 e.除了定义/实现类Show,难道没有函数可以接受各种类型的对象并向其附加
.Show()
函数吗?当然,您需要能够以非反射方式将新字段附加到记录,而ps没有。类与显式函数字典的不同之处在于编译器负责为您传递它们。如果没有类型类,大多数函数都会附加额外的参数,这将非常难以使用,并使重载运算符(例如
>=
)不可能实现。就我个人而言,我不太喜欢这种方法,但人们已经探索了很多。比如说,我在想那个。虽然我认为可能有一种方法结合了两者的优点,但需要一种新的语言。仍在策划。接下来,我被我策划的语言卡住了。无论如何,typescript很好地支持这一点,
[{x:0},{x:1,y:'a'}]
的类型就是
[{x:int}{x:int,y:char}]
,它只允许您附加这些类型,并且识别出您总是可以得到
x
purescript存在的问题是,您无法约束类型。如果需要对类型进行约束(如我的回答中的
Show
),则必须将约束烘焙到
Exists
的定义中,这与它是一个可重用库的想法大相径庭。这是另外一个问题,即它不是一种多态性,您必须使用复制粘贴来解决。我不确定我是否理解您在@FyodorSoikin.*上下文中的评论在最初的问题@DaxFohl中,我问如何创建一个共享一些已知字段的记录集合,这在我的回答中已经很清楚了。我们不必使用此represe拖动任何约束
x :: Array [ Int ]
x = map (runExists (unwrap >>> _.x)) arr