首页 > 解决方案 > 在 Haskell 中调度联合类型

问题描述

我正在尝试在 Haskell 中编写一个基于 String 或 Num 参数的多态函数。

我写了一些无法编译的伪代码:

numDigits strOrNum = 
  if isString strOrNum then length strOrNum
  else isNum strOrNum then length (show strOrNum)

numDigits 1000   -- Should return 4
numDigits "1000" -- Should return 4

请注意isStringisNum它们不是真正的 Haskell 函数,它们仅用于演示。

来自 Lisp,strOrNum 是一个联合类型,这个代码可以运行。

我知道 Haskell 中的临时多态性需要类型类,但我不确定如何将它拼凑在一起。

标签: haskelltypestypeclass

解决方案


将某些东西建模为 sum-type 还是 type-class 更好,我认为您只是随着时间的推移而建立的直觉(sum-types 通常是更好的选择)。

但既然你提到

我知道 Haskell 中的临时多态性需要类型类,但我不确定如何将它拼凑在一起。

这是一个有希望的直观解释。

基本理念

基本上,您是说您的代码可以使用多种类型。您应该尝试制定这些类型的共同点——即为什么您接受这些类型但拒绝其他类型。然后,您定义一个类型类来捕获这一点,并根据该类型类实现您的大部分代码。

你的例子

在你的例子中说明它。您正在接受数字和字符串(表示数字),因为您说两者都可以表示为数字序列,并且您想要定义一个计算这些数字的函数。那么定义一个类型类来捕获被写为数字序列的能力可能是有意义的。

class IsBase10Positional t where
  digits :: t -> [Int]

然后,您将为您的类型定义实例:

instance IsBase10Positional Int where
    digits n = D.digits 10 n

instance IsBase10Positional String where
    digits chars = digitToInt <$> chars

D.digits来自http://hackage.haskell.org/package/digits-0.3.1/docs/Data-Digits.html#v:digits

digitToInt来自https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-Char.html#v:digitToInt

numDigits然后在属于此类型类的所有类型上定义您的函数:

numDigits :: IsBase10Positional a => a -> Int
numDigits x = length (digits x)

结论

通常很难制定类型类应该捕获的常见行为。如果您开始将大量函数添加到类型类本身中,那么您可能无法捕捉到类型共有的正确本质(在您的应用程序的域中)。


推荐阅读