多变量广义和

时间:2013-04-30 19:19:25

标签: haskell polyvariadic

This answer演示了一个多变量函数,该函数对其参数求和:

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

我为Num的所有成员创建了此函数的通用版本:

class (Num n) => MySumType n r where
    mySum :: n -> r

instance (Num n) => MySumType n n where
    mySum x = x

instance (Num n, MySumType n r) => MySumType n (n->r) where
    mySum x = mySum . (x +)

但是,这仅适用于mySum (1::Int) (3::Int) (2::Int) :: Int之类的调用。如果没有参数的类型说明符,我会收到此错误:

  

使用“mySum”时没有(MySumType n0 Float)的实例

     

可能的解决方法:为(MySumType n0 Float)

添加实例声明      

在表达式中:mySum 1.0 :: Float

     

在'it'的等式中:it = mySum 1.0 :: Float

导致此问题的原因是什么?如何解决?我怀疑它与Num a => a的整数文字类型有关。还有一个与上面不依赖于扩展的功能相同的功能吗?上面使用的是多个参数类型类和灵活的实例。

1 个答案:

答案 0 :(得分:5)

我还没有遇到过针对haskell中的多变量函数的引人注目的用例,因为使用列表或类似的数据结构无法解决这个问题。因此,如果你认为你有一个超越新奇的东西,我之所以玩它们的原因,我会很高兴知道它是什么。下面给出了足够的例子,其中一些我在评论时应该考虑过,我已经撤回了我的陈述。

{-# language MultiParamTypeClasses #-}
{-# language FlexibleInstances #-}
{-# language TypeFamilies #-}
{-# language IncoherentInstances #-}

class (Num n) => MySumType n r where
    mySum :: n -> r

instance (Num n, m~n) => MySumType n m where
    mySum x = x

instance (Num n, MySumType n r, n~m) => MySumType n (m->r) where
    mySum x = mySum . (x +)

然后将文件加载到ghci:

> mySum 1 2 4 5 6 7 :: Int
25
> mySum 1.1 2 4.6 5 6.9 7 :: Double
26.6

在某些情况下,类型推断也可能是您的朋友,允许您删除最终类型注释,如下面的设计案例:

> replicate (mySum 1 2 3 4) 6
[6,6,6,6,6,6,6,6,6,6]

关于:

  

还有一个与上面不依赖于扩展的功能相同的功能吗?

我觉得你运气不好。我想要注意的是,除非你有理由放弃GHC或继续使用Haskell98或Haskell2010扩展,否则你不会造成任何伤害并导致很少的兼容性问题,因为大多数人似乎都在使用GHC。

编辑:补充说明

让我们开始解释更简单实例之间的区别。我将把postfix 2添加到我提供的实现的一些名称中。

instance (Num n) => MySumType n n where
    mySum x = x

如果你将它与类声明结合起来:

class (Num n) => MySumType n r where
    mySum :: n -> r

mySum的类型签名为mySum :: (Num n) => n -> nn -> n表示采用n类型的1个arity函数,生成nn具有Num类。

使用此mySum时,我必须指定我提供的内容及其产生的内容。

mySum 1 :: Int
mySum (1 :: Int)

只有在指定输入和输出类型时才会给出错误,它会给出结果:

mySum (1 :: Int) :: Int
            ^       ^
            |       specify output type
            specify input type

产生结果(在这种情况下为1)。这是因为您已为n -> n提供了一个实例,但有些人可能会在n -> m添加Int -> Double的实例,如下所示:

instance MySumType Int Double where
    mySum x = 2 * fromIntegral x

> mySum (1::Int) :: Int
1
> mySum (1::Int) :: Double
2.0

这些实例都匹配所有可能的1个arity函数类型的非常窄的区域。

现在让我们看一下修改后的版本

instance (Num n, m~n) => MySumType n m where
    mySum x = x

mySum此处有类型签名mySum :: (Num n, m~n) => n -> m,此类型签名适用于所有1个类型n的arity函数,并生成类型m n类为Num且m等于n。请注意,这开始将所有1个arity函数n -> m,任何n与任何m匹配,然后在其上添加反对。

现在可以这样做:

> mySum2 (1::Int)
1
> mySum2 1 :: Int
1

指定输入后,也会指定输出。其他说明12

这并不能阻止我们像Int -> Double这样具体的更具体的例子:

instance MySumType Int Double where
    mySum x = 2 * fromIntegral x

> mySum2 1 :: Int
1
> mySum2 1 :: Double
1.0
> mySum2 (1 :: Int) :: Double
2.0

只有最后一个mySum2正在处理Int -> Double实例。这是IncoherentInstances的属性,我想我会将其留给another stackoverflow question来回答IncoherentInstances正在播放的角色。