在Haskell中表示类型类的任意实现

时间:2015-06-06 21:40:27

标签: haskell typeclass

我正在尝试克服多年在经典Java风格的继承模型中工作,以真正适应Haskell。它进展不顺利,我需要一些帮助。

假设我有一个类型类Foo。我想表示Foo的任意实现类的列表,但不是限制列表中的每个项都相同;我需要一个异构的开放类型,以便我的库的消费者可以实现他们自己的Foo

之所以如此,是因为我想写下面的内容(pidgin Haskell):

class Foo -- something...

data Bar = Bar Int Char
data Baz = Baz String

instance Foo Bar
instance Foo Baz

saySomething :: Foo -> String
saySomething (Bar x _) = "Bar number " ++ (show x)
saySomething (Baz x) = "Your baz is " ++ x
saySomething _ = "Unknown Foo"

sayAll :: [Foo] -> [String]
sayAll = map (saySomething)

main = putStrLn $ sayAll [ (Bar 5 'k'), (Bar 7 'G'), (Baz "hello") ]

我如何创建一个可扩展的类型类(或其他数据类型),可以与同一类型类的其他类型自由混合,但不一定是完全相同的类型?

2 个答案:

答案 0 :(得分:2)

您所看到的是Heterogenous collections

恕我直言,最好的方法是使用存在感,如wiki所述:

{-# LANGUAGE ExistentialQuantification #-}

data Showable = forall a . Show a => MkShowable a

pack :: Show a => a -> Showable
pack = MkShowable

hlist :: [Showable]
hlist = [ pack 3
        , pack 'x'
        , pack pi
        , pack "string"
        , pack (Just ()) ]

答案 1 :(得分:2)

您尝试做(异构)收集的问题是:一旦您有Foo列表,就很难回到原始类型。但是,如果您愿意忘记原始类型,则解决问题的另一种方法是数据转换为Foo。这种方法可能看似错误,但请记住,数据在Haskell中是不可变的,因此您无论如何都无法修改对象,因此您对Foo唯一能做的就是从中获取信息。然后,像Bar这样的真实Foo和充当Foo版本的新Bar之间没有区别(也是,Haskell懒惰,所以只有在需要时才会进行转换

当你意识到这一点时,你甚至可以更进一步取代object但只是一堆功能(如@chi链接中所述)。您的代码变为

 data Foo = { saySomething :: String, saySomethingElse :: String }
 -- Haskell is lazzy, so saySomething can be seen as function
 -- without argument

 class Fooable a where
       toFoo :: a -> Foo

 data Bar = Bar Int Char
 data Baz = Bar String

 instance Fooable Bar where
        toFoo (Bar i c) = { "Bar number : " ++ show i, "Bar char : " ++ [c] }
 instance Fooable Baz where
        toFoo (Baz s) = {"Your baz is " ++ s, "Nothing to add." }

 sayAll : [Foo] -> [String]
 sayAll = map saySomething

 bar = Bar 1 'a'
 baz = Baz "Bazzar"

 sayAll [toFoo bar, toFoo baz]

请注意,即使somethingElse看起来不像一个函数,但它只是一个普通的函数,它永远不会被调用(在本例中)。 toFoo的结果可以被视为BarFoo之间的网桥