在Haskell中表示typeclass的任意实现
我正在努力克服多年来在经典Java风格继承模型中的工作,以真正熟悉Haskell。事情进展不顺利,我需要一些帮助 假设我有一个typeclass在Haskell中表示typeclass的任意实现,haskell,typeclass,Haskell,Typeclass,我正在努力克服多年来在经典Java风格继承模型中的工作,以真正熟悉Haskell。事情进展不顺利,我需要一些帮助 假设我有一个typeclassFoo。我想表示一个任意实现类的列表Foo,但不能限制列表中的每一项都是相同的;我需要一个异构的、开放的类型,以便我的库的使用者能够实现他们自己的Foo 原因是因为我想写以下内容(pidgin Haskell): 我如何创建一个可扩展的typeclass(或其他数据类型),它可以与相同typeclass的其他数据类型自由混合,但不一定是相同的确切类型?您
Foo
。我想表示一个任意实现类的列表Foo
,但不能限制列表中的每一项都是相同的;我需要一个异构的、开放的类型,以便我的库的使用者能够实现他们自己的Foo
原因是因为我想写以下内容(pidgin Haskell):
我如何创建一个可扩展的typeclass(或其他数据类型),它可以与相同typeclass的其他数据类型自由混合,但不一定是相同的确切类型?您正在寻找的是
IMHO最好的方法是使用存在主义,如维基上所述:
{-# 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 ()) ]
您尝试(异构)收集的问题在于:一旦您有了
Foo
列表,就很难回到原始类型。但是,如果您乐于忘记原始类型,另一种解决问题的方法是将数据转换为Foo
。这种方法可能看起来是错误的,但请记住,数据在Haskell中是不可变的,因此无论如何您都无法修改对象,因此您可以使用Foo
s的唯一方法就是从对象中获取信息。因此,像Foo
那样的真正Bar
和像Bar
版本那样的新Foo
之间没有区别(同样,Haskell lazy,因此只有在需要时才会进行转换)
当您意识到这一点时,您甚至可以进一步替换对象
,但只需替换一组函数(如@chi-link中所述)。您的代码变为
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
之间的桥梁这是一个-[Showable]
和[String]
之间没有实际区别,后者更简单。你是对的,但是如果typeclass提供了不止一个方法呢?事实上,我的观点不像卢克·帕尔默在他的博客中所表达的那样激进,但我确实认为他有道理。存在类型类模式在某些情况下很有用,但很容易被滥用。在Foo
上会有多个方法。感谢@utdemir提供的答案,@chi提供了Luke Palmer文章的链接,因为一旦我了解了概念,我可能会重写我的代码。可扩展类型类是什么意思?类型类定义了一个类。例如类Eq a,其中(==),(/=)::a->a->Bool
意味着Eq
的实例类型必须实现(==)
和(/=)
。这是为了确保他们会有这些行动。我不明白你的意思。
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]