如何用两个模板参数编写 class,其中一个是另一个的 list/array?
How is it possible to write a class with two template parameters, where one is a list/array of the other?
我想用 Clean(一种与 Haskell 非常相似的语言)解决这个问题:
有一个 class Node t
,有两个实例:instance Node EdgeList
和 instance Node Adjacency
。我想创建一个图形,它是一个数组或节点列表。
Graph
的定义是:
class Graph t1 t2 | Node t2 where
resetGraph :: (t1 t2) -> (t1 t2)
graphSize :: (t1 t2) -> Int
...
我想写实例。一种是数组,另一种是列表。首先,我尝试使用列表,但出现错误:t2 not defined
instance Graph [t1] t2 | t2 t1 where
(resetGraph) :: [t1] -> [t1]
(resetGraph) x = []
...
它会被这样调用:resetGraph listAdj
其中 listAdj 是 Adjacency
个节点的列表
如果我只写:instance Graph [tt] tt
然后我得到这个错误:Error: this type variable occurs more than once in an instance type
.
这里首先要明白的是,当你写
class Graph l t | Node t where
resetGraph :: (l t) -> l t
你给l
种类*->*
。种类是类型的抽象。粗略地说,kind *
意味着你有一个 'complete' 类型。例如Int
、[Char]
、a -> String
都是*
的种类。当类型仍然是 'needs an argument' 时,它的种类是 *->*
。例如,如果您有 :: Maybe a = Just a | Nothing
,那么 Maybe Int
属于 *
,但 Maybe
属于 *->*
,因为它仍然需要一个参数。所以,当写 resetGraph :: (l t) -> l t
时,编译器认为 t
是 l
的参数,所以唯一的方法是给 resetGraph
类型 *
(这是必要的对于一个函数),就是给 l
kind *->*
(和 t
kind *
)。
您需要知道的第二件事是 [Char]
、(Int,Int)
和 a -> Real
类型也可以写成前缀:[] Char
、(,) Int Int
、(->) a Real
。你可以比较 []
和 Maybe
:它仍然需要一个参数(这里是 Char
)是一个完整的类型。因此,类型 []
具有种类 *->*
。类似地,(,)
有种类 *->*->*
,因为它仍然需要两个类型才能完整,(->)
也是如此。 (注意:这在 language report 的第 4.5 节中有记录)。
结合这两个,你应该写:
instance Graph [] Adjacency where
...
然后,resetGraph
的类型解析为([] Adjacency) -> [] Adjacency
,与[Adjacency] -> [Adjacency]
相同。
对于数组,前缀表示法是 {} Adjacency
for {Adjacency}
。
顺便说一句:在 StdEnv
中用 class length
:
完成了类似的事情
// StdOverloaded.dcl
class length m :: !(m a) -> Int
// StdList.icl
instance length [] where ...
我想用 Clean(一种与 Haskell 非常相似的语言)解决这个问题:
有一个 class Node t
,有两个实例:instance Node EdgeList
和 instance Node Adjacency
。我想创建一个图形,它是一个数组或节点列表。
Graph
的定义是:
class Graph t1 t2 | Node t2 where
resetGraph :: (t1 t2) -> (t1 t2)
graphSize :: (t1 t2) -> Int
...
我想写实例。一种是数组,另一种是列表。首先,我尝试使用列表,但出现错误:t2 not defined
instance Graph [t1] t2 | t2 t1 where
(resetGraph) :: [t1] -> [t1]
(resetGraph) x = []
...
它会被这样调用:resetGraph listAdj
其中 listAdj 是 Adjacency
个节点的列表
如果我只写:instance Graph [tt] tt
然后我得到这个错误:Error: this type variable occurs more than once in an instance type
.
这里首先要明白的是,当你写
class Graph l t | Node t where
resetGraph :: (l t) -> l t
你给l
种类*->*
。种类是类型的抽象。粗略地说,kind *
意味着你有一个 'complete' 类型。例如Int
、[Char]
、a -> String
都是*
的种类。当类型仍然是 'needs an argument' 时,它的种类是 *->*
。例如,如果您有 :: Maybe a = Just a | Nothing
,那么 Maybe Int
属于 *
,但 Maybe
属于 *->*
,因为它仍然需要一个参数。所以,当写 resetGraph :: (l t) -> l t
时,编译器认为 t
是 l
的参数,所以唯一的方法是给 resetGraph
类型 *
(这是必要的对于一个函数),就是给 l
kind *->*
(和 t
kind *
)。
您需要知道的第二件事是 [Char]
、(Int,Int)
和 a -> Real
类型也可以写成前缀:[] Char
、(,) Int Int
、(->) a Real
。你可以比较 []
和 Maybe
:它仍然需要一个参数(这里是 Char
)是一个完整的类型。因此,类型 []
具有种类 *->*
。类似地,(,)
有种类 *->*->*
,因为它仍然需要两个类型才能完整,(->)
也是如此。 (注意:这在 language report 的第 4.5 节中有记录)。
结合这两个,你应该写:
instance Graph [] Adjacency where
...
然后,resetGraph
的类型解析为([] Adjacency) -> [] Adjacency
,与[Adjacency] -> [Adjacency]
相同。
对于数组,前缀表示法是 {} Adjacency
for {Adjacency}
。
顺便说一句:在 StdEnv
中用 class length
:
// StdOverloaded.dcl
class length m :: !(m a) -> Int
// StdList.icl
instance length [] where ...