Dyl*_*lan 6 haskell polyvariadic
这个答案演示了一个多变量函数,它总结了它的参数:
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
Run Code Online (Sandbox Code Playgroud)
我为以下所有成员创建了此函数的通用版本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 +)
Run Code Online (Sandbox Code Playgroud)
但是这仅适用于类似的调用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.还有一个与上面不依赖于扩展的功能相同的功能吗?上面使用的是多个参数类型类和灵活实例.
我还没有遇到过针对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 +)
Run Code Online (Sandbox Code Playgroud)
然后将文件加载到ghci后:
> mySum 1 2 4 5 6 7 :: Int
25
> mySum 1.1 2 4.6 5 6.9 7 :: Double
26.6
Run Code Online (Sandbox Code Playgroud)
在某些情况下,类型推断也可以是您的朋友,允许您删除最终类型注释,如下面的设计案例:
> replicate (mySum 1 2 3 4) 6
[6,6,6,6,6,6,6,6,6,6]
Run Code Online (Sandbox Code Playgroud)
关于:
还有一个与上面不依赖于扩展的功能相同的功能吗?
我觉得你运气不好.我想指出,除非你有理由放弃GHC或继续使用Haskell98或Haskell2010扩展,否则你不会造成任何伤害并导致很少的兼容性问题,因为大多数人似乎都在使用GHC.
让我们开始解释更简单实例之间的区别.我将把postfix 2添加到我提供的实现的一些名称中.
instance (Num n) => MySumType n n where
mySum x = x
Run Code Online (Sandbox Code Playgroud)
如果将此与类声明结合使用:
class (Num n) => MySumType n r where
mySum :: n -> r
Run Code Online (Sandbox Code Playgroud)
mySum有一个类型签名mySum :: (Num n) => n -> n.这就是说n -> n1个arity函数接受一个类型n,产生一个n并且n有一个Num类.
使用这个时,mySum我必须指定我给它的东西以及它产生的东西.
mySum 1 :: Int
mySum (1 :: Int)
Run Code Online (Sandbox Code Playgroud)
只有在指定输入和输出类型时它才会给出错误,它会给出结果:
mySum (1 :: Int) :: Int
^ ^
| specify output type
specify input type
Run Code Online (Sandbox Code Playgroud)
产生结果(在这种情况下为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
Run Code Online (Sandbox Code Playgroud)
这些实例各自匹配所有可能的1个arity函数类型的非常窄的区域.
现在让我们看一下修改后的版本
instance (Num n, m~n) => MySumType n m where
mySum x = x
Run Code Online (Sandbox Code Playgroud)
mySum这里有类型签名,mySum :: (Num n, m~n) => n -> m这个类型签名是针对所有1个arity函数,它们采用类型n和生成类型m,其中n类具有Num并且m等于n.请注意,这开始匹配所有1个arity函数n -> m,任何n对任何m然后添加反对它.
现在可以这样做:
> mySum2 (1::Int)
1
> mySum2 1 :: Int
1
Run Code Online (Sandbox Code Playgroud)
这并没有阻止我们从特定的更具体的实例中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
Run Code Online (Sandbox Code Playgroud)
只有最后一个mySum2正在处理Int -> Double实例.这是属性,IncoherentInstances我想我会把它留给另一个stackoverflow问题来回答IncoherentInstances正在播放的角色.