我正在尝试编码一些项目列表,这些项目的类型仅限于某些类型类的实例:
{-# LANGUAGE RankNTypes, TypeSynonymInstances, LiberalTypeSynonyms #-}
module Test where
class Someable a where
some :: a -> String
data Some = Some String
type SomeGroup = forall a. Someable a => [a]
instance Someable Some where
some (Some v) = v
instance Someable SomeGroup where
some (x:xs) = (some x) ++ ", " ++ (some xs)
main = do
putStrLn $ show.some [Some "A", [Some "B", Some "C"]]
但是编译失败并出现错误:
Test.hs:14:10:
Illegal polymorphic or qualified type: SomeGroup
In the instance declaration for `Someable SomeGroup'
似乎我甚至没能为类型synonymous定义实例...
我知道heterogenous collections wiki文章,但是想知道为什么我的方法不起作用 - 通过限制集合来定义类型来定义类型似乎很自然,因为它包含的类型是某种类型。
答案 0 :(得分:9)
如果我理解正确的事情,就需要有一个数据类型包装存在性,以便有一个地方存储type class dictionary以及每个元素。
添加一些包装器使其工作:
{-# LANGUAGE ExistentialQuantification, TypeSynonymInstances #-}
module Test where
class Someable a where
some :: a -> String
data Some = Some String
data SomeWrapper = forall a. Someable a => SomeWrapper a
type SomeGroup = [SomeWrapper]
instance Someable Some where
some (Some v) = v
instance Someable SomeWrapper where
some (SomeWrapper v) = some v
instance Someable SomeGroup where
some (x:xs) = (some x) ++ ", " ++ (some xs)
main = do
putStrLn $ some [SomeWrapper (Some "A"), SomeWrapper [SomeWrapper (Some "B"), SomeWrapper (Some "C")]]
当然,这有点难看。不幸的是,我不知道有任何更好的方法。
答案 1 :(得分:3)
您也可以使用GADT烹饪。在某些情况下,这可能会稍微缩短一些,并且在模式匹配后明确指出哪些类型的字典可用。
以下是您示例的略微变体:
{-# LANGUAGE GADTs #-}
class Someable a where
some :: a -> String
instance Someable Int where
some n = show n
data SomeString = SomeString String
instance Someable SomeString where
some (SomeString s) = s
data SomeGroup where
Nil :: SomeGroup
Cons :: Someable a => a -> SomeGroup -> SomeGroup
instance Someable SomeGroup where
some Nil = ""
some (Cons x Nil) = some x
some (Cons x xs) = some x ++ ", " ++ some xs
list = Cons (3::Int) (Cons (SomeString "abc") (Cons (42::Int) Nil))
main = print . some $ list
一些小调:
putStrLn . show
与print
相同。Int
,因为整数文字是专门处理的,即42
被翻译为fromInteger 42
类型的Num a => a
。直接构建列表时有点笨拙,但大多数时候它会更顺畅地工作。Cons
元素的语法糖定义到列表中,但语法永远不会像Haskell的内置语法一样好! 重点是你失去了所有标准列表功能的使用。当我的列表处理需求非常有限时,我通常使用这种解决方案;另一方面,折叠写得足够快......你的里程会有所不同,我不知道你真正想要使用这个列表 。