代码之家  ›  专栏  ›  技术社区  ›  Muhammad Alkarouri

纯函数式编程环境中的面向对象编程?

  •  41
  • Muhammad Alkarouri  · 技术社区  · 15 年前

    在函数式编程(FP)上下文中使用面向对象编程(OOP)有什么好处吗?

    F# 一段时间以来,我注意到我的函数越是无状态的,就越不需要它们作为对象的方法。特别是,依赖类型推断使它们在尽可能多的情况下可用是有好处的。

    这并不排除对某种形式的名称空间的需求,这种形式与OOP是正交的。也不鼓励使用数据结构。事实上,FP语言的真正使用在很大程度上依赖于数据结构。如果你看看在 F Sharp Programming/Advanced Data Structures ,您会发现它不是面向对象的。

    在我看来,OOP与拥有对对象状态起作用的方法密切相关 对象。在不需要也不需要的纯FP上下文中。

    .NET . 除此之外,还有什么原因吗?在Haskell世界,编程是更纯粹的FP,你有什么经验?

    3 回复  |  直到 13 年前
        1
  •  54
  •   C. A. McCann Ravikant Cherukuri    15 年前

    你看到的断开不是FP和OOP。它主要是关于不变性和数学形式主义对易变性和非正式的方法。

    首先,让我们不要讨论易变性问题:可以让FP具有易变性,而让OOP具有不变性。甚至比Haskell更实用的是,你可以随心所欲地处理可变数据,你只需要明确什么是可变的,以及事情发生的顺序;撇开效率不谈,几乎任何可变对象都可以构造并返回一个新的“更新”实例,而不是改变它自己的内部状态。

    如果去掉形式主义的不匹配,许多明显的冲突就会消失。想在LISP之上构建一个灵活的、动态的、ad-hoc的OO系统吗?去吧,一切都会好的。想在ML风格的语言中添加一个形式化的、不可变的OO系统吗?没问题,只是不要期望它能很好地与.NET或Java一起使用。


    面向对象程序设计的适当形式主义?好吧,这里有一句妙语:在很多方面,它比ML风格的FP更以功能为中心!我回头再看 one of my favorite papers 这似乎是一个关键的区别:像ML风格语言中的代数数据类型这样的结构化数据提供了数据的具体表示和对其定义操作的能力;对象提供了 行为 以及容易更换部件的能力。

    the Expression Problem :对于具体的数据,您可以轻松地添加使用它的新操作,但更改数据的结构更为困难。使用对象,您可以轻松地添加新的数据(例如,新的子类),但添加新的操作是困难的(考虑向具有许多子类的基类添加新的抽象方法)。

    例如,这种类型的设计在Haskell中得到了很好的应用,请阅读 the graphics-drawingcombinators package ,特别是它使用包含函数的不透明记录类型的方式,并且只根据函数的行为组合事物。


    最后几件事我忘了在上面提到。

    如果OO确实广泛地使用了高阶函数,那么一开始看起来它应该非常自然地适合于Haskell这样的函数语言。不幸的是,事实并非如此。的确 物体 正如我所描述的那样(参见LtU链接中提到的文章)非常合适。事实上,结果是一种比大多数OO语言更纯粹的OO风格,因为“私有成员”由用于构造“对象”的闭包隐藏的值来表示,并且除了一个特定实例本身之外,其他任何对象都无法访问。你没有比这更隐私的了!

    在哈斯克尔工作不太好的是 子类型

    新的

    在Haskell特定的注释中,它的“类型类”对于OO程序员来说很有诱惑力,对此我说:不要去那里。尝试以这种方式实现OOP只会以失败告终。把类型类看作重载函数/运算符的替代,而不是OOP。

        2
  •  8
  •   Heatsink    15 年前

    至于Haskell,类在那里不那么有用,因为一些OO特性更容易以其他方式实现。

    data RNG a where RNG :: (seed -> (a, seed)) -> seed -> RNG a

    参数多态性或“泛型编程”上下文中的动态方法调度由类型类(不是OO类)提供。类型类类似于OO类的虚拟方法表。但是,没有数据隐藏。类型类不像类方法那样“属于”数据类型。

    data Coordinate = C Int Int
    
    instance Eq Coordinate where C a b == C d e = a == b && d == e
    

    -- An "abstract base class" with two "virtual methods"
    data Object =
      Object
      { draw :: Image -> IO ()
      , translate :: Coord -> Object
      }
    
    -- A "subclass constructor"
    circle center radius = Object draw_circle translate_circle
      where
        -- the "subclass methods"
        translate_circle center radius offset = circle (center + offset) radius
        draw_circle center radius image = ...
    
        3
  •  6
  •   Peter Mortensen Pieter Jan Bonestroo    13 年前

    我认为有几种方法可以理解OOP的含义。对我来说,这不是封装 可变状态 ,但更多关于组织和组织程序的内容。OOP的这一方面可以很好地与FP概念结合使用。

    我相信把F这两个概念混合起来是非常有用的方法,你可以联想到。 不变的

    type GameWorld(characters) = 
      let calculateSomething character = 
        // ...
      member x.Tick() = 
        let newCharacters = characters |> Seq.map calculateSomething
        GameWorld(newCharacters)
    

    一开始,人们通常不会用F#声明类型——你可以从编写函数开始,然后发展你的代码来使用它们(当你更好地理解域并知道什么是构造代码的最佳方式时)。上面的例子:

    • 仍然是纯功能性的(状态是一个字符列表,并且没有改变)
    • 它是面向对象的-唯一不寻常的事情是所有方法都返回一个新的“世界”实例
    推荐文章