有没有办法做类似的事情
first = {x:0}
second = {x:1,y:1}
both = [first, second]
Run Code Online (Sandbox Code Playgroud)
这样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
Run Code Online (Sandbox Code Playgroud)
我能想到的一切似乎都告诉我,不支持将这样的记录合并到一个集合中。有点像,函数可以是多态的,但列表不能。这是正确的解释吗?它让我想起了 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
Run Code Online (Sandbox Code Playgroud)
如果你有多个这样的操作,你可以随时记录它们:
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
}
Run Code Online (Sandbox Code Playgroud)
(注意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
Run Code Online (Sandbox Code Playgroud)
它的工作方式是RecordRep包装一个函数,该函数接受另一个函数,该函数是多态的r——也就是说,如果您正在查看 a RecordRep,您必须准备好给它一个可以与 any 一起使用的函数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
Run Code Online (Sandbox Code Playgroud)
show像这样传递函数是有效的,因为我们r以Show {x::Int|r}必须存在的方式约束了行,因此,应用show到{x::Int|r}必须起作用。根据需要重复您自己的类型类。
这是有趣的部分:因为类型类是作为函数字典实现的,所以上面描述的两个选项实际上是等效的——在这两种情况下,你最终都会传递一个函数字典,只有在第一种情况下它是明确的,但在第二种情况下如果编译器为你做这件事。
顺便说一句,这也是 Haskell 语言支持的工作方式。
| 归档时间: |
|
| 查看次数: |
327 次 |
| 最近记录: |