4

我正在尝试在 Haskell 中创建一个多变量函数,我使用这个答案来创建一个基本函数。这是函数的代码:

class SumRes r where 
    sumOf :: Integer -> r

instance SumRes Integer where
    sumOf = id

instance (Integral a, SumRes r) => SumRes (a -> r) where
    sumOf x = sumOf . (x +) . toInteger

但问题是:当函数在没有任何参数的情况下被调用时,它不起作用。

Couldn't match expected type 'Integer' with actual type 'Integer -> r0'
    Probable cause: 'sumOf' is applied to too few arguments

例如,我希望能够编写sumOf :: Integer并让这个函数返回0

我该怎么做?

4

1 回答 1

7

最简单的版本只适用于Integer结果。

简单的方法

0这可以解决您已经编写的内容,利用加法标识这一事实。

class SumRes r where
  sumOf' :: Integer -> r

instance SumRes Integer where
  sumOf' = toInteger

instance (Integral b, SumRes r) => SumRes (b -> r) where
  sumOf' a b = sumOf' $! a + toInteger b

sumOf :: SumRes r => r
sumOf = sumOf' 0

两个实例Integerb -> r本质上不重叠。

更难的方式

要获得更一般的结果类型,您需要一种稍微不同的方法,因为上面描述的两个实例如果Integer被类型变量替换,则会混合在一起。您可以使用MultiParamTypeClasses和来做到这一点TypeFamilies

{-# LANGUAGE ScopedTypeVariables, AllowAmbiguousTypes, DataKinds,
      KindSignatures, TypeApplications, MultiParamTypeClasses,
      TypeFamilies, FlexibleInstances #-}

module SumRes2 where

data Nat = Z | S Nat
class SumRes (c :: Nat) r where
  sumOf' :: Integer -> r

type family CountArgs a :: Nat where
  CountArgs (_ -> r) = 'S (CountArgs r)
  CountArgs _ = 'Z

instance Num r => SumRes 'Z r where
  sumOf' = fromInteger

instance (Integral b, SumRes n r) => SumRes ('S n) (b -> r) where
  sumOf' a b = sumOf' @n (a + toInteger b)

sumOf :: forall r n. (SumRes n r, CountArgs r ~ n) => r
sumOf = sumOf' @n 0

唯一的限制是,如果你有Integral一个函数类型的实例,你就不能用它sumOf生成它。不过,这应该不是问题。为简洁起见,我使用了TypeApplicationsand AllowAmbiguousTypes,但您当然可以使用代理传递或Tagged代替。

于 2017-09-06T16:31:40.087 回答