类型项限制的类型项列表

时间:2011-06-08 20:41:11

标签: haskell typeclass heterogeneous

我正在尝试编码一些项目列表,这些项目的类型仅限于某些类型类的实例:

{-# 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文章,但是想知道为什么我的方法不起作用 - 通过限制集合来定义类型来定义类型似乎很自然,因为它包含的类型是某种类型。

2 个答案:

答案 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 . showprint相同。
  • 您必须明确说明数字的类型为Int,因为整数文字是专门处理的,即42被翻译为fromInteger 42类型的Num a => a。直接构建列表时有点笨拙,但大多数时候它会更顺畅地工作。
  • 您当然可以将Cons元素的语法糖定义到列表中,但语法永远不会像Haskell的内置语法一样好!

重点是你失去了所有标准列表功能的使用。当我的列表处理需求非常有限时,我通常使用这种解决方案;另一方面,折叠写得足够快......你的里程会有所不同,我不知道你真正想要使用这个列表