【问题标题】:Does Haskell support object oriented programmingHaskell 是否支持面向对象编程
【发布时间】:2020-03-11 22:08:28
【问题描述】:

它是否支持声明和实现分离(Java 中的接口和类)之类的概念?

如何限制访问(如 Java 中的访问修饰符)?

【问题讨论】:

  • 请注意,如果您在 Haskell 中工作,则不应尝试编写 Java 程序,反之亦然。 语言一起工作,而不是逆向工作,通常会产生更清晰的代码。
  • 这是我做的一个小练习,试图模仿一些面向对象的特性。 gist.github.com/877617 但是,这不过是一个练习。我不会以 Java 风格编写 Haskell。
  • 下面有一些很棒的答案,但我建议您不要将此类事情视为“OO”功能。例如,在 Haskell 中,封装和隐藏实现的主要单元是模块,并且不依赖于任何特定的数据结构。模块化设计在用于面向对象编程之前是一件好事!同样,Haskell 中的类型类明确拒绝将数据结构及其行为写在一个地方的 OO 思想,但仍然有效地将接口和实现分开。
  • AFAIK 即使在 C 中,您也可以划分声明和实现。访问限制也不是真正的 OOP 概念。数据隐藏可能是,但这更多的是关于如何使用事物,而不是语言施加的限制。

标签: haskell


【解决方案1】:

在 Haskell 中如何分离声明和实现?

在 Haskell 中,您可以定义一个 typeclass,这与面向对象的类有很大不同,所以不要被这个名字欺骗了。使用关键字class,您可以声明函数名称和类型签名,它们可以在其他地方为特定数据类型实例化(实现)。

例如,Hashable 类型类定义了hash 函数,该函数可以将任何实例化的数据类型转换为Int。有一个新的、时髦的数据类型,您希望能够散列?好吧,做一个 Hashable 的实例。最常见的数据类型由定义 Hashable 的模块实例化(参见“实例”的链接文档)。

类型类并不是定义接口的唯一方法。一种经常被低估的方法是一种普通的旧数据结构。因为 Haskell 有一流的函数,你可以定义一个数据结构,将函数作为字段:

data ShuttleInterface =
  SI { launch    :: Delay -> IO Handle
     , deploy    :: Payload -> IO ()
     , getStatus :: IO Status
     }

您的函数可以构建或使用此数据结构:

deployAllSensors :: ShuttleInterface -> IO ()
deployAllSensors shuttle = do
    status <- getStatus shuttle
    let notDeployed = filter (not . deployed) (sensors status)
    when (isOrbiting status) (mapM_ deploySensor notDeployed)

-- we used the well-known Haskell functions: filter, not, , when, mapM_
-- and some supporting functions were assumed:
isOrbitting :: Status -> Bool
deploySensor :: Sensor -> IO ()
sensors :: Status -> [Sensor]
deployed :: Sensor -> Bool

如何限制对 Haskell 中数据的访问?

为了提供抽象,Haskell 使用Algebraic Data Types。为了保护字段,开发人员声明了一种数据类型,但不导出它的构造函数——相反,他们只导出一组维护所需不变量的安全原语。

例如,Map 模块提供了平衡树。如果任何人都可以使用BranchLeaf 的原语声明地图,它无法保证平衡,因此制造商没有导出这些。地图的构建必须依赖于从 Data.Map 导出的内容(并且那些由于位于同一模块中而可以访问/使用构造函数),例如 fromListemptysingleton 和一个整体一堆修饰符。

【讨论】:

  • +1,也许值得指出的是,由于所有数据都是不可变的,因此尽管可能,但对字段的保护并不像在具有可变数据类型的 OOP 语言中那样重要。
  • 我在 Haskell 中发现了另一个有用的“类 OOP”语法示例:stackoverflow.com/questions/24235757/…
  • @Ingo 需要的不是防止修改的能力,而是防止依赖的能力。就像sun.misc.Unsafe 所示。可以说,OOP 是关于抽象内部结构。
  • 即使您具有不变性,在模块中隐藏实现细节仍然是一种很好的做法。
  • 请注意,类型类不能替代 OO 类或 OO 接口(如在 Java 中),这一点非常重要!它们可能被命名并且看起来很相似,但是认为您可以将一个用于另一个将使您陷入混乱。相信我;我沿着那条路走了。
【解决方案2】:

有关如何在 Haskell 中实现 OO 概念的详细说明,请参阅 Oleg Kiselyov 和 Ralf Laemmel 的 Haskell's Overlooked Object System。但正如 Antal 在 cmets 中所说,不要尝试用 Haskell 编写 Java 程序。

请记住,对象是穷人的闭包,而闭包是穷人的对象。

【讨论】:

  • 你可以把闭包想象成一个单一方法的临时对象;就像一个对象一样,它用一个动态绑定的函数包装了一堆数据,该函数将使用它。所以对于 OO 程序员来说,闭包看起来有点像一种非常有限的对象。另一方面,如果你想在 Java 中创建一个类似闭包的东西,那么你需要创建一个特殊的基类,然后为每个版本的闭包创建一个新的后代。因此,从函数式程序员的角度来看,对象看起来像是一种非常笨拙和冗长的闭包。
  • 或者对象和闭包提供相同的功能,我们都是穷人;-)
  • @PaulJohnson 您的链接已损坏。你能给出论文的实际名称吗?
  • @dylan,我相信 Paul J. 指的是“Haskell 的被忽视对象系统”,可以从 arXiv.org 联系到 here
  • 根据@caya 的有用评论,我编辑了评论以包括论文名称和链接。
【解决方案3】:

类型类确实是唯一能远程提醒 OO 概念的结构——在这种情况下,是在接口上。不过,与 java 不同,类型类不是类型。

关于类型类的一个好处是我可以使完全不相关的、已经存在的类型成为类型类的成员。而在 java 中,有时人们会想:我正在使用的包 org.a 中的这些类 A 和 com.b 中的 B 确实应该从第三个包中实现接口 Y,但是没有办法做到这一点,不需要大量样板代码、附加间接、编组等。

顺便说一句,作为一名年长的程序员,我想指出“声明和实现的分离”本身与 OOP 无关。仅仅因为大多数 OO 语言都支持它并不意味着该概念在 OO 发明之前很长一段时间内并不为人所知。感兴趣的年轻人如果认为 OO 成为主流之前的编程一定已经处于“石器时代”的水平,可以查看 MODULA,例如,声明和实现的分离不仅是可能的,而且由语言强制执行。

【讨论】:

    【解决方案4】:

    值得一提的是lenses。它们允许您以可组合的方式编写a.b.c.d.e“表达式”。

    可以为每个数据结构定义.。所以在某种意义上,. 是 Haskell 的一等公民。可以命名、存储,可以组合两个.-s等

    【讨论】:

      【解决方案5】:

      我只是偶然发现了某事,请参阅https://github.com/complyue/ooh 以获得具有完整机器的可运行程序(尽管完整代码仍然非常短)。

      我从没想过OO可以用Haskel写出时尚的程序 如此直接!

      构造对象:

      !o <- classC $^ (777, "hahah")
      

      调用直接方法:

        cx0 <- o $. getNumC $ ()
        o $. setNumC $ 888
      

      调用基础方法:

        bx0 <- (o $.. cast'C'as'B) getNumB ()
        (o $.. cast'C'as'B) setNumB 999
      

      方法参数必须是 uncurried 作为原型在这里,我想它是 不过,在编写 OO 样式代码时更可取。

      虽然对象类定义现在看起来很冗长,就像手工制作的一样, 我相信 Template Haskell 可以提供很多更好的语法糖。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-09
        • 1970-01-01
        • 1970-01-01
        • 2016-07-07
        • 2010-09-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多