15

我正在尝试对类型限制为某些类型类的实例的项目列表进行编码:

{-# 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'

看来我什至没有为类型同义词定义实例......

我知道异构集合wiki 文章,但想知道为什么我的方法不起作用 - 通过将集合限制为仅包含具有某种类型类实例的类型的项目来定义类型对我来说似乎很自然。

4

2 回答 2

9

如果我理解正确,则需要有一个数据类型来包装存在,以便有一个地方来存储类型类字典以及每个元素。

添加一些包装器使其工作:

{-# 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")]]

当然,这有点难看。不幸的是,我不知道有什么更好的方法。

于 2011-06-08T20:57:51.033 回答
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 42type Num a => a。直接构建列表时有点笨拙,但大多数时候它会更顺利地工作。
  • 您当然可以定义自己的语法糖,用于将Cons元素添加到列表中,但语法永远不会像 Haskell 的内置语法那样好!

一个重点是你失去了对所有标准列表函数的使用。当我的列表处理需求非常有限时,我通常会使用这种解决方案;另一方面,折叠的速度足够快......虽然你的里程会有所不同,但我不知道你真正想用这个列表什么。

于 2011-06-09T15:36:08.360 回答