我正在尝试克服多年在经典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") ]
我如何创建一个可扩展的类型类(或其他数据类型),可以与同一类型类的其他类型自由混合,但不一定是完全相同的类型?
答案 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
的结果可以被视为Bar
和Foo
之间的网桥。