我们如何在 Haskell 中实现模块化

How can we implement modularity in Haskell

在 OOP 中,我们可以使用 'interface' 来要求对象实现某些方法。我认为这可以在 FP Haskell 中实现,但我正在努力获得一个工作示例。

我的目标是创建一个具有一些标准化接口的工具包,以确保项目领域内的兼容性和标准化。

例如,假设我的一个接口定义了一个计算器,以及它必须实现的方法;我们可以要求每个计算器实现 + 和 -。可以有许多不同版本的计算器,它们可以根据需要定义函数。下面是一些类似于 Haskell 的代码尝试。

class Calculator c where
  add :: (Float a) => a -> a -> a
  sub :: (Float a) => a -> a -> a

instance Calculator DumbCalc where
  add a b = 5
  sub a b = 3

instance Calculator SmartCalc where
  add a b = a + b
  sub a b = a - b

通过以这种方式使用接口,我可以创建一个模块化程序,该程序利用没有硬编码行为的组件。在上面的示例中,我可以创建一个包含计算器的程序;以后我可以创建一个流程更高效的新计算器,并在不改变父程序逻辑的情况下实现它。

如何在 Haskell 中实现此设计模式?

如果只有操作而没有类型,就像您的示例一样,您更有可能将其视为数据类型。

data Calculator = Calculator { 
   add :: Float -> Float -> Float, 
   sub :: Float -> Float -> Float
  }

构建计算器:

dumbCalc :: Calculator
dumbCalc = Calculator (\_ _ -> 5) (\_ _ -> 3)

smartCalc :: Calculator
smartCalc = Calculator (+) (-)

你甚至可以组合计算器,这是模块化的真正秘诀。

-- Uses both calculators and returns whichever result is smaller
leastCalc :: Calculator -> Calculator -> Calculator
leastCalc c1 c2 = Calculator (\x y -> min (add c1 x y) (add c2 x y))
                             (\x y -> min (sub c1 x y) (sub c2 x y))

而需要计算器的函数会将 Calculator 作为参数,您可以根据需要将其传递给它们。

如果除了函数之外还对类型进行抽象,那么像这样的数据类型模块化将无法正常工作。在那种情况下,正确的工具是 class 类型,因为这通常是类型 class 的要点,我将让您在网上的无数可用资源中阅读这些内容。