我有一个像这样的数据构造函数
class FooClass a where
foo :: a -> b
class BarClass a where
bar :: a -> b
data FooBar = Foo :: FooClass a => a -> IO ()
| Bar :: BarClass a => a -> IO ()
这样我就可以使用模式匹配了:
foobar :: FooBar -> a -> IO ()
foobar (Foo f) x = f (foo x)
foobar (Bar f) x = f (bar x)
然而,这违反了开放/封闭原则
我希望能够使用基于其他类的其他方法扩展FooBar
。
我如何在Haskell中实现它?
答案 0 :(得分:2)
正如其他人所指出的那样,这段代码存在一些模糊不清的问题。试图过分考虑OO原则如何转化为FP也可能是危险的。他们有一个地方,因为大部分的OO都是自然地嵌入到FP中,但是最好先直接学习FP,然后在某些特殊情况下遵守法则。
特别是,我们可以讨论如何更好地改进类型是一种扩展形式。例如,比较像
这样的类型(Num a) => a -> IO ()
(Num a, Show a) => a -> IO ()
我们可以讨论第二个函数如何采用一组类型,这是第一个函数输入的自然子类型。特别地,可以输入到第二函数的一组可能类型是对第一函数的输入的细化。作为这些功能的用户,使用第二功能的有效方法较少。作为这些功能的实现者,有更多有效的方法来实现第二个功能。事实上,我们知道以下内容
Game semantics的研究探讨了给予与服用之间的这种二元性。 “开放延伸”的想法非常简单,因为我们总是可以决定要求更精致的类型,但它几乎完全无趣,因为在使用精炼类型方面这是显而易见的。
那么直接针对ADT(data
声明)呢?然后打开/关闭? Mu - ADT不是对象,因此该规则不直接适用。
答案 1 :(得分:2)
在Haskell中执行示例的技巧是使用函数而不是类:
-- FooBar is like a base class
-- with methods foo and bar.
-- I've interpreted your example liberally
-- for purposes of illustration.
-- In particular, FooBar has two methods -
-- foo and bar - with different signatures.
data FooBar = FooBar {
foo :: IO (),
bar :: Int -> Int
}
-- Use functions for classes, like in Javascript.
-- This doesn't mean Haskell is untyped, it just means classes are not types.
-- Classes are really functions that make objects.
fooClass :: Int -> FooBar
fooClass n = FooBar {
foo = putStrLn ("Foo " ++ show n)
bar = \n -> n+1
}
barClass :: FooBar
barClass = FooBar {
foo = putStrLn "Bar ",
bar = \n -> n * 2
}
-- Now we can define a function that uses FooBar and it doesn't matter
-- if the FooBar we pass in came from fooClass, barClass or something else,
-- bazClass, say.
foobar (FooBar foo bar) = do
-- invoke foo
foo
-- use bar
print (bar 7)
此处FooBar
是'开放式扩展',因为我们可以使用不同的行为创建尽可能多的FooBar
值。
使用其他字段“FooBar
”baz
“延伸”FooBar
,而不更改fooClass
,barClass
或FooBarBaz
,我们需要声明{{1}包含FooBar
的类型。我们仍然可以使用foobar
函数,我们只需首先从FooBar
中提取FooBarBaz
。
到目前为止,我一直在接近OOP。这是因为Bertrand Meyer提出了开放的封闭原则,要求OOP或类似的东西:
软件实体(类,模块,函数等)应该是开放的 用于扩展,但已关闭以进行修改
特别是,“扩展”一词传统上被解释为“子类化”。如果您准备将原理解释为仅仅“具有扩展点”,那么将另一个函数作为参数的任何函数都是“打开以进行扩展”。这在函数式编程中非常常见,因此不被视为原理。 “参数化原则”听起来并不一样。