纯函数式编程上下文中的面向对象编程?

时间:2010-11-06 12:49:33

标签: oop scala haskell f# functional-programming

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

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

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

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

一个实际的原因可能是能够与OOP代码交互,就像F#与.NET一样工作。除此之外,有什么理由吗? Haskell世界的经验是什么,编程是更纯粹的FP?

我将非常感谢有关该问题的论文或反事实现实世界的例子。

3 个答案:

答案 0 :(得分:53)

答案 1 :(得分:8)

至于Haskell,类在那里不太有用,因为某些OO功能更容易以其他方式实现。

封装或“数据隐藏”通常通过功能闭包或存在类型而不是私有成员来完成。例如,这是具有封装状态的随机数发生器的数据类型。 RNG包含生成值和种子值的方法。因为类型'seed'是封装的,所以你唯一可以做的就是将它传递给方法。

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

在子类型多态或“子类化”的上下文中的动态方法分派几乎是使用记录和函数在Haskell中对类模式的转换。

-- 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 = ...

答案 2 :(得分:6)

我认为有几种方法可以理解OOP的含义。对我来说,它不是关于封装可变状态,而是关于组织和构建程序的更多信息。 OOP的这个方面可以与FP概念完美结合使用。

我认为在F#中混合使用这两个概念是一种非常有用的方法 - 您可以将不可变状态与处理该状态的操作相关联。您将获得标识符'点'完成的好功能,易于使用C#中的F#代码等功能,但您仍然可以使您的代码完全正常运行。例如,您可以编写如下内容:

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

一开始,人们通常不会在F#中声明类型 - 你可以通过编写函数开始,然后进化代码来使用它们(当你更好地理解域并知道构造代码的最佳方法时) 。上面的例子:

  • 仍然是纯粹的功能(状态是一个字符列表,它没有变异)
  • 它是面向对象的 - 唯一不寻常的是所有方法都返回“世界”的新实例