如何声明多态集合类型的标记联合

How declare tagged union of polymorphic collection types

我是 Purescript 的新手。我目前的学习练习是创建多态数组和列表的标记联合。我将在查找任何数组或列表长度的函数中使用它。这是我的尝试:

import Data.List as L
import Data.Array as A

data Collection = CollectionList (forall a. L.List a) 
                | CollectionArray (forall b.  Array b)

colLength :: Collection -> Int
colLength (CollectionList list) = L.length list
colLength (CollectionArray arr) = A.length arr

main :: Effect Unit
main = do
   logShow (colLength (CollectionArray [3,5]))

编译器不喜欢:

 Could not match type Int with type b0     

 while checking that type Int is at least as general as type b0
 while checking that expression 3 has type b0
 in value declaration main

 where b0 is a rigid type variable

我对 checking that type Int is at least as general as type b0b0 is a rigid type variable 这两个部分感到困惑。我的意图是让 b 成为任何东西。不确定我做了什么让编译器为 b 设置条件。

如果您知道如何操作,请展示定义将在我的 colLength 函数中工作的标记的多态类型并集的正确方法。

forall a 并不意味着“任何类型都在这里”

这意味着谁访问这个值,谁就可以选择a是什么,谁提供这个值就拥有以确保该值属于该类型。这是提供者和消费者之间的合同。

因此,当您提供值 CollectionArray [3,5] 时,您必须使其能够 对所有 可能 a 以后访问该值的人有效可能会选择。

显然,只有一种方法可以构建这样的值:

CollectionArray []

您可能 实际上 打算做的(我在这里猜测)是使您的集合多态化,因为它可以包含任何类型的值,但是类型由 创建 集合的人选择,然后访问它的人必须处理该特定类型。

为此,您必须将类型变量放在外面:

data Collection a = CollectionList (L.List a) 
                  | CollectionArray (Array a)

这样,当你创建一个集合 CollectionArray [3,5] 时,它变成了 Collection Int 类型,现在你传递它的任何地方,比如 colLength,都必须处理那个Int

反过来,这可以通过使 colLength 本身通用来实现:

colLength :: forall a. Collection a -> Int
colLength (CollectionList list) = L.length list
colLength (CollectionArray arr) = A.length arr

现在无论谁访问(即调用)colLength 本身都可以选择 a 是什么,这很好用,因为它是最初创建 Connection Int 的同一个地方.