首页 文章

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

提问于
浏览
3

我正在尝试克服多年在经典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") ]

How would I create an extensible typeclass (or other datatype) that can be freely mixed with others of the same typeclass, but not necessarily the same exact type?

2 回答

  • 2

    你在找什么Heterogenous collections

    恕我直言,最好的方法是使用存在,如维基上所述:

    {-# 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 ()) ]
    
  • 2

    你正在尝试做什么(异类)收集的问题是:一旦你有 Foo 的列表,很难回到原始类型 . 但是,如果您乐于忘记原始类型,另一种解决问题的方法是将数据转换为 Foo . 这种方法可能看似错误,但请记住,数据在Haskell中是不可变的,所以你永远无法修改你的对象,所以你可以用你的 Foo 来获取信息 . 然后,像 Foo 这样的真实 Bar 和作为 Bar 版本的新 Foo 之间没有区别(同样,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 之间的桥梁 .

相关问题