在 Haskell 中调度联合类型

Cal*_*ies 0 haskell types typeclass

我正在尝试编写一个函数,该函数在 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
Run Code Online (Sandbox Code Playgroud)

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

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

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

fgh*_*ini 9

无论是将某些东西建模为 sum 类型还是类型类更好,我认为您只是随着时间的推移而建立的一种直觉(和类型通常是更好的选择)。

但既然你提到

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

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

基本理念

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

你的榜样

用你的例子来说明它。您接受数字和字符串(表示数字),因为您说两者都可以表示为数字序列,并且您想定义一个对这些数字进行计数的函数。然后定义一个类型类来捕获写入数字序列的能力可能是有意义的。

class IsBase10Positional t where
  digits :: t -> [Int]
Run Code Online (Sandbox Code Playgroud)

然后你会为你的类型定义实例:

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

instance IsBase10Positional String where
    digits chars = digitToInt <$> chars
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

结论

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


Car*_*ten 5

你可以这样工作:

data MyNum
  = IntNum Int
  | StrNum String

numDigits :: MyNum -> Int
numDigits (IntNum n) = length $ show n
numDigits (StrNum s) = length s

instance Num MyNum where
  fromInteger = IntNum . fromInteger
  -- note: skipped the rest (won't work really well sorry)

instance IsString MyNum where
  fromString = StrNum
Run Code Online (Sandbox Code Playgroud)

这是 GHCi 中的一个示例:

> :set -XOverloadedStrings
> numDigits "1000"
4
> numDigits 1000
4
Run Code Online (Sandbox Code Playgroud)

这是因为1000文字被认为是 any 的类型Num-fromInteger将用于将其转换为目标类型。

同样的"1000",当您启用OverloadedStrings在GHC这将作为一个被视为扩展IsString实例使用fromString

我认为这只是这个问题的一个很好的技巧-Num实例显然是不完整的。

我不得不考虑一下,但我认为由于部分原因(例如允许),这不能变成合法的 Num实例StrNumStrNum "bad"

我的直觉是你遇到麻烦,因为你必须使用StrNum ...值都01如果这些都相等,所产生的环真的只能是{0}(如果你甚至允许的话)

注意:这不是证据,我可以(并且可能)错了......