基本问题:在使用类或使用记录(具有多态字段)之间进行选择时,应遵循哪些设计原则?

首先,我们知道类和记录在本质上是等效的(因为在Core中,类成为字典的字典,而字典只是记录)。尽管如此,还是有区别的:类是隐式传递的,记录必须是显式的。

再深入一点,在以下情况下,类确实很有用:

  • 我们对“同一件事”有许多不同的表示形式,而
  • 可以推断出
  • 的实际用法,即使用了哪种表示形式。

  • 当我们只有(仅参数多态性)数据的一种表示形式时,类很尴尬,但是我们有多个实例。如果我们不想打开所有麻烦的扩展名,这会导致不得不使用 newtype 来添加额外的标签(仅存在于我们的代码中,因为我们知道这样的标签在运行时会被删除),从而产生语法噪音。 (即重叠和/或不确定的实例)。

    当然,事情变得更加困惑:如果我想限制自己的类型怎么办?我们来看一个真实的例子:
    class (Bounded i, Enum i) => Partition a i where
        index :: a -> i
    

    我可以轻松完成
    data Partition a i = Partition { index :: a -> i}
    

    但是现在我已经失去了约束,而必须将它们添加到特定的函数中。

    有设计准则对我有帮助吗?

    最佳答案

    我倾向于只需要对功能进行约束就不会出现问题。我想问题是,您的数据结构不再精确地按照您的意图建模。另一方面,如果您首先将其视为数据结构,则该问题就不那么重要了。

    我觉得我不一定仍然对这个问题有很好的了解,这大概是模糊的,但是我的经验法则往往是类型类是遵守法律(或模型含义)的事物,而数据类型是数据类型是编码一定数量信息的事物。

    当我们想以复杂的方式对行为进行分层时,我发现类型类开始时很诱人,但很快就会变得痛苦起来,切换到字典传递使事情变得更加简单。这就是说,当我们希望实现可以互操作时,我们应该退回到统一的字典类型。

    这需要两个时间,在一个具体示例上进行了一些扩展,但是仍然只是一些旋转的想法...

    假设我们要对实数上的概率分布建模。我想到了两种自然的表述。

    A)类型类驱动

    class PDist a where
            sample :: a -> Gen -> Double
    

    B)字典驱动
    data PDist = PDist (Gen -> Double)
    

    前者让我们做
    data NormalDist = NormalDist Double Double -- mean, var
    instance PDist NormalDist where...
    
    data LognormalDist = LognormalDist Double Double
    instance PDist LognormalDist where...
    

    后者让我们做
    mkNormalDist :: Double -> Double -> PDist...
    mkLognormalDist :: Double -> Double -> PDist...
    

    在前者中,我们可以写
    data SumDist a b = SumDist a b
    instance (PDist a, PDist b) => PDist (SumDist a b)...
    

    在后者中,我们可以简单地写
    sumDist :: PDist -> PDist -> PDist
    

    那么,权衡是什么呢?类型类驱动使我们可以指定给出的分布。折衷方案是我们必须显式构造分布的代数,包括它们组合的新类型。数据驱动不允许我们限制给定的分布(即使它们的格式正确),但作为返回,我们可以做任何我们想做的事情。

    此外,我们可以相对轻松地编写parseDist :: String -> PDist,但是我们必须经历一些焦虑才能实现类型类方法的等效。

    因此,从某种意义上讲,这是另一级别的有类型/无类型的静态/动态折衷。但是,我们可以给它一个曲折,并认为类型类以及相关的代数法则指定了概率分布的语义。而且,PDist类型确实可以成为PDist类型类的实例。同时,我们可以辞职在几乎所有地方都使用PDist类型(而不是类型类),而将其视为实例塔和数据类型的同等体,以便更“丰富”地使用类型类。

    实际上,我们甚至可以根据类型类函数定义基本的PDist函数。即mkNormalPDist m v = PDist (sample $ NormalDist m v)因此设计空间中有很多空间可以根据需要在两个表示之间滑动...

    关于haskell - 在类(class)和记录之间选择,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8106764/

    10-16 12:31