数据构造函数没有违反开放/封闭原则

时间:2014-06-20 10:42:03

标签: haskell constructor open-closed-principle

我有一个像这样的数据构造函数

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中实现它?

2 个答案:

答案 0 :(得分:2)

正如其他人所指出的那样,这段代码存在一些模糊不清的问题。试图过分考虑OO原则如何转化为FP也可能是危险的。他们有一个地方,因为大部分的OO都是自然地嵌入到FP中,但是最好先直接学习FP,然后在某些特殊情况下遵守法则。

特别是,我们可以讨论如何更好地改进类型是一种扩展形式。例如,比较像

这样的类型
(Num a)         => a -> IO ()
(Num a, Show a) => a -> IO ()

我们可以讨论第二个函数如何采用一组类型,这是第一个函数输入的自然子类型。特别地,可以输入到第二函数的一组可能类型是对第一函数的输入的细化。作为这些功能的用户,使用第二功能的有效方法较少。作为这些功能的实现者,有更多有效的方法来实现第二个功能。事实上,我们知道以下内容

  1. 作为第二个功能的有效输入的所有值也是第一个
  2. 的有效输入
  3. 第一个签名正确输入的所有函数也会被第二个签名正确输入。
  4. 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值。

使用其他字段“FooBarbaz“延伸”FooBar,而不更改fooClassbarClassFooBarBaz,我们需要声明{{1}包含FooBar的类型。我们仍然可以使用foobar函数,我们只需首先从FooBar中提取FooBarBaz

到目前为止,我一直在接近OOP。这是因为Bertrand Meyer提出了开放的封闭原则,要求OOP或类似的东西:

  

软件实体(类,模块,函数等)应该是开放的   用于扩展,但已关闭以进行修改

特别是,“扩展”一词传统上被解释为“子类化”。如果您准备将原理解释为仅仅“具有扩展点”,那么将另一个函数作为参数的任何函数都是“打开以进行扩展”。这在函数式编程中非常常见,因此不被视为原理。 “参数化原则”听起来并不一样。