以通用方式作为实例的一组函数

Set of functions that are instances in a common way

我是 haskell 的新手,我想我陷入了一些面向对象的陷阱。这是我在实现时遇到问题的结构草图(简化):

我的第一个想法是使用 subclass;类似的东西(下面有点做作,但希望能理解这个想法)

class Observable a where
  estimate :: a -> [Int] -> Int

class (Observable a) => SimpleObservable a where
  compute :: a -> Int -> Int
  simpleEstimate :: a -> [Int] -> Int
  simpleEstimate obs list = sum $ map compute list

data AveConst = AveConst Int

instance Observable AveConst where
  estimate = simpleEstimate

instance SimpleObservable AveConst  where
  compute (AveConst c) x = c * x

然而,即使像上面这样的东西编译起来也很难看。谷歌搜索告诉我 DefaultSignatures 可能会有所帮助,因为我不必为每个实例都做 estimate = simpleEstimate 但从围绕它的讨论来看,这样做似乎是一种反模式。

另一种选择是没有子class,而是类似(具有相同的Observable class):

data AveConst = AveConst Int

instance Observable AveConst where
  estimate (AveConst c) list = sum $ map (*c) list

但是这样我不确定如何重用模式;每个 Observable 必须包含完整的 estimate 定义,并且会有代码重复。

第三种方式是带有函数字段的类型:

data SimpleObservable = SimpleObservable {
  compute :: [Int] -> Int
} 

instance Observable SimpleObservable where
  estimate obs list =
    sum $ map (compute obs) list

aveConst :: Int -> SimpleObservable
aveConst c = SimpleObservable {
  compute = (*c)
}

但我也不确定这是否符合习惯。有什么建议吗?

我还没有完全理解这个问题,但我会在了解更多信息后编辑这个答案。

作用于列表并产生结果的东西可以简单地是一个函数。这个函数的接口(也就是类型)可以是[a] -> b。这表示该函数接受某种类型的元素列表,并且 returns 可能是不同类型的结果。

现在,让我们发明一个小问题作为例子。我想要一个列表列表,列表上的一些函数产生一个数字,将这个函数应用于每个列表,然后 return 数字的平均值。

average :: (Fractional b) => ([a] -> b) -> [[a]] -> b
average f xs = sum (fmap f xs) / genericLength xs

例如,average genericLength 会告诉我子列表的平均长度。我不需要定义任何类型 类 或新类型。简单地说,我将函数类型 [a] -> b 用于那些将列表映射到某个结果的函数。

我建议更简单:

type Observable = [Int] -> Int

那么,平均可观察量是:

average :: Observable
average ns = sum ns `div` length ns

如果您的 Observable 需要一些内部数据——比如,一个常量乘以——没问题;这就是闭包的目的。例如:

sumTimesConst :: Int -> Observable
sumTimesConst c = sum . map (c*)

您可以毫不费力地抽象出 Observable 的构造;例如如果你想要一个只查看元素然后求和的SimpleObservable,你可以:

type SimpleObservable = Int -> Int

timesConst :: Int -> SimpleObservable
timesConst = (*)

liftSimple :: SimpleObservable -> Observable
liftSimple f = sum . map f

那么 liftSimple . timesConst 是另一种拼写 sumTimesConst.

的完美方式

...但老实说,我觉得做上述任何事情 都很脏。 sum . map (c*) 是一个完全可读的表达式,没有为其类型引入有问题的新名称。