【问题标题】:Achieving the right abstractions with Haskell's type system使用 Haskell 的类型系统实现正确的抽象
【发布时间】:2011-08-27 13:53:28
【问题描述】:

我无法优雅地使用 Haskell 的类型系统。我确信我的问题很常见,但我不知道如何描述它,除非用我的程序特定的术语。

我试图表达的概念是:

  • 数据点,每个数据点采用多种形式之一,例如(id, 病例数, 对照数), (id, 病例数, 人口)

  • 数据点集和聚合信息:(id 集、案例总数、控件总数),具有添加/删除点的功能(因此对于每种不同的点,都有相应的不同集合)

我可以有一类点类型并将每种点定义为自己的类型。或者,我可以为每个品种设置一个点类型和不同的数据构造函数。对于点集也是如此。

我对每种方法至少有一个顾虑:

  • 使用类型类:避免函数名冲突会很烦人。例如,两种类型的点都可以使用一个函数来提取“案例数”,但是类型类不能要求这个函数,因为其他一些点类型可能没有案例。

  • 没有类型类:我宁愿不从 Point 模块导出数据构造函数(提供其他更安全的函数来创建新值)。如果没有数据构造函数,我将无法确定给定 Point 值的种类。

什么样的设计可以帮助减少这些(和其他)问题?

【问题讨论】:

  • 在什么情况下需要对数据点进行类似处理?您说每种形式都需要自己的各种集合,并且诸如“案例数”之类的操作不是通用的。

标签: haskell types functional-programming


【解决方案1】:

为了扩展 sclv 的答案,有一个扩展系列密切相关的概念,相当于提供了一些解构值的方法:Catamorphisms,它们是广义折叠; Church-encoding,通过操作来表示数据,通常相当于对它解构的值部分应用变态; CPS 转换,其中 Church 编码类似于一个具体化的模式匹配,它为每种情况采用单独的延续;将数据表示为使用它的操作的集合,通常称为面向对象编程;等等。

在您的情况下,您似乎想要的是一个抽象类型,即不导出其内部表示,但不是完全密封的表示,即使表示对定义它的模块中的函数。这与Data.Map.Map 之类的内容相同。您可能想走类型类路线,因为听起来您需要处理各种数据点,而不是任意选择单一类型的数据点。

最有可能的是,“智能构造函数”的某种组合来创建值,以及从模块中导出的各种解构函数(如上所述)是最好的起点。从那里开始,我希望剩下的大部分细节应该有一个明显的下一步要采取的方法。

【讨论】:

    【解决方案2】:

    使用后一种解决方案(无类型类),您可以导出类型而不是构造函数的变态..

    data MyData = PointData Double Double | ControlData Double Double Double | SomeOtherData String Double
    
    foldMyData pf cf sf d = case d of
           (PointData x y) -> pf x y
           (ControlData x y z) -> cf x y z
           (SomeOtherData s x) -> sf s x
    

    这样,您就可以将数据拆分为您想要的任何内容(包括忽略值并传递返回您使用的构造函数类型的函数),而无需提供构造数据的通用方法。

    【讨论】:

      【解决方案3】:

      只要您不打算在单个数据结构中混合不同的数据点,我发现基于类型类的方法会更好。

      您提到的名称冲突问题可以通过为每个不同的字段创建单独的类型类来解决,如下所示:

      class WithCases p where
          cases :: p -> NumberOfCases 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-22
        • 2020-04-12
        • 2011-07-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多