Haskell中的多变量函数 [英] Polyvariadic Functions in Haskell

查看:199
本文介绍了Haskell中的多变量函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在阅读这篇关于在Haskell中编写多变量函数的文章后,我试图写一些我自己的。

起初,我想我会试图推广它 - 所以我可以有一个函数,通过折叠参数返回可变参数函数。 / b>

  { - #选项-fglasgow-exts# - } 
模块折叠其中
class折叠ar | r - > a where
collapse ::(a - > a - > a) - > a - > r
实例折叠a其中
折叠_ = id
实例(折叠a r)=>折叠(a - > r)其中
折叠fa a'=折叠f(fa a')

然而,编译器不喜欢:

  Collapse.hs:5:9:
实例声明之间的函数依赖冲突:
实例折叠aa - 定义于Collapse.hs:5:9-20
实例(折叠ar)=>折叠a(a - > r)
- 定义于Collapse.hs:7:9-43

如果我返回并为最终结果添加了包装类型,它就起作用了:

  module Collapse其中
class Collapse ar | r - > a where
collapse ::(a - > a - > a) - > a - > r
data C a = C a
实例折叠a(C a)其中
折叠_ = C。 id
实例(折叠a r)=>折叠a(a - > r)其中
折叠f a a'=折叠f(f a a')
sum ::(Num a,Collapse a r)=> a - > r
sum = collapse(+)

一旦我做了这个改变,我可以在 ghci 中使用 collapse 函数。

  ghci的>让C s = Collapse.sum 1 2 3 in s 
6

我不是确定为什么最终结果需要包装类型。如果有人能解释这一点,我会非常感激。我可以看到编译器告诉我这是函数依赖关系的一些问题,但我没有真正理解fundeps的正确使用。



稍后,我尝试采取不同的方法,并尝试为取得列表并返回值的函数定义可变函数发生器。我必须做同样的容器技巧,并且还允许 UndecidableInstances

  { - #选项-fglasgow-exts# - } 
{ - #LANGUAGE UndecidableInstances# - }
模块Variadic其中
class Variadic abr | r - > a,r - > b其中
variadic ::([a] - > b) - > r
data V a = V a
instance Variadic a b(V b)其中
variadic f = V $ f []
instance(Variadic a b r)=>变量a b(a - > r)其中
variadic f a = variadic(f。(a :))
list:Variadic a [a] r => r
list = variadic。 id
foldl ::(Variadic b a r)=> (a→b→a)→> a - > r
foldl fa = variadic(Prelude.foldl fa)

不允许<$ c $

  Variadic.hs:7: 0:
'Variadic ab(V b)'
的非法实例声明(覆盖条件失败,其中一个函数依赖;
使用-XUndecidableInstances来允许)
In 'Variadic ab(V)'的实例声明'

Variadic.hs:9:0:
'Variadic ab(a - > r)'的非法实例声明'
(Coverage条件失败,其中一个函数依赖;
使用-XUndecidableInstances来允许这样做)
在`Variadic ab(a - > r)'的实例声明中'

但是,一旦编译完成,我可以在ghci中成功使用它:

  ghci的>让V l = Variadic.list 1 2 3 in l 
[1,2,3]
ghci>让vall p = Variadic.foldl(\ b a - > b&&(p a))True
ghci> :t vall
vall ::(Variadic b Bool r)=> (b→Bool)→> r
ghci>在b
真实



中设置V b = vall(> 0)1 2 3猜想我在寻找的是解释为什么最终值的容器类型是必要的,以及为什么所有各种函数依赖都是必需的



另外,这看起来很奇怪:

  ghci> let vsum = Variadic.foldl(+)0 

< interactive>:1:10:
模糊类型变量约束中的'a','r':
`在< interactive>:1:10-29
处使用'Variadic.foldl'引起的变化aa r'
可能的修复:添加修复这些类型变量的类型签名

< interactive>:1:10:
约束中含糊不清的类型变量`a':
'< interactive> 1:29
可能的修复:添加一个修复这些类型变量的类型签名
ghci>让vsum'= Variadic.foldl(+)
ghci> :t vsum'
(Num a,Variadic a a r)=> t - > a - > r
ghci> :t vsum'0
(Num a,Variadic a a r)=> a - > r
ghci>让V s = vsum'0 1 2 3 in s
6

我猜这是从允许 UndecidableInstances 的后果,但我不知道,我想更好地理解发生了什么。

解决方案

函数依赖关系背后的想法是,在一个声明中,比如

  class Collapse ar | r  - > a 
...

r - >一个位表示 a r 唯一确定。因此,您不能有实例Collapse(a - > r)(a - > r)和实例折叠a(a - > ; r)。请注意,从实例下面的实例Collapse(a - > r)(a - > r)对于完整的图片。

换句话说,您的代码正在试图建立实例Collapse tt (类型变量的名字当然是不重要的)和实例折叠(a - > r)。如果在第一个实例声明中用(a - > r)代替 t ,则得到实例Collapse(a - > r)(a - > r)。现在这是 Collapse 的唯一实例,第二个参数等于(a - > r)你可以拥有,因为类声明表示第一个类型参数可以从第二个类型中推导出来。然后,接下来尝试建立实例a(a - > r),它将添加另一个 Collapse 实例第二个类型参数是(a - > r)。因此,GHC抱怨。

After reading this article on writing polyvariadic functions in Haskell, I tried to write some of my own.

At first I thought I'd try to generalize it - so I could have a function that returned variadic functions by collapsing arguments as given.

{-# OPTIONS -fglasgow-exts #-}
module Collapse where
class Collapse a r | r -> a where
  collapse :: (a -> a -> a) -> a -> r
instance Collapse a a where
  collapse _ = id
instance (Collapse a r) => Collapse a (a -> r) where
  collapse f a a' = collapse f (f a a')

However, the compiler didn't like that:

Collapse.hs:5:9:
    Functional dependencies conflict between instance declarations:
      instance Collapse a a -- Defined at Collapse.hs:5:9-20
      instance (Collapse a r) => Collapse a (a -> r)
        -- Defined at Collapse.hs:7:9-43

If I went back and added a wrapper type for the final result, however, it worked:

module Collapse where
class Collapse a r | r -> a where
  collapse :: (a -> a -> a) -> a -> r
data C a = C a
instance Collapse a (C a) where
  collapse _ = C . id
instance (Collapse a r) => Collapse a (a -> r) where
  collapse f a a' = collapse f (f a a')
sum :: (Num a, Collapse a r) => a -> r
sum = collapse (+)

Once I made this change, it compiled fine, and I could use the collapse function in ghci.

ghci> let C s = Collapse.sum 1 2 3 in s
6

I'm not sure why the wrapper type is required for the final result. If anyone could explain that, I'd highly appreciate it. I can see that the compiler's telling me that it's some issue with the functional dependencies, but I don't really grok the proper use of fundeps yet.

Later, I tried to take a different tack, and try and define a variadic function generator for functions that took a list and returned a value. I had to do the same container trick, and also allow UndecidableInstances.

{-# OPTIONS -fglasgow-exts #-}
{-# LANGUAGE UndecidableInstances #-}
module Variadic where
class Variadic a b r | r -> a, r -> b where
  variadic :: ([a] -> b) -> r
data V a = V a
instance Variadic a b (V b) where
  variadic f = V $ f []
instance (Variadic a b r) => Variadic a b (a -> r) where
  variadic f a = variadic (f . (a:))
list :: Variadic a [a] r => r
list = variadic . id
foldl :: (Variadic b a r) => (a -> b -> a) -> a -> r
foldl f a = variadic (Prelude.foldl f a)

Without allowing UndecidableInstances the compiler complained that my instance declarations were illegal:

Variadic.hs:7:0:
    Illegal instance declaration for `Variadic a b (V b)'
        (the Coverage Condition fails for one of the functional dependencies;
         Use -XUndecidableInstances to permit this)
    In the instance declaration for `Variadic a b (V b)'

Variadic.hs:9:0:
    Illegal instance declaration for `Variadic a b (a -> r)'
        (the Coverage Condition fails for one of the functional dependencies;
         Use -XUndecidableInstances to permit this)
    In the instance declaration for `Variadic a b (a -> r)'

However, once it compiled, I could successfully use it in ghci:

ghci> let V l = Variadic.list 1 2 3 in l
[1,2,3]
ghci> let vall p = Variadic.foldl (\b a -> b && (p a)) True
ghci> :t vall
vall :: (Variadic b Bool r) => (b -> Bool) -> r
ghci> let V b = vall (>0) 1 2 3 in b
True

I guess what I'm looking for is an explanation of why the container type for the final value is necessary, as well as why all the various functional dependencies are necessary.

Also, this seemed odd:

ghci> let vsum = Variadic.foldl (+) 0

<interactive>:1:10:
    Ambiguous type variables `a', `r' in the constraint:
      `Variadic a a r'
        arising from a use of `Variadic.foldl' at <interactive>:1:10-29
    Probable fix: add a type signature that fixes these type variable(s)

<interactive>:1:10:
    Ambiguous type variable `a'in the constraint:
      `Num a' arising from the literal `0' at <interactive>:1:29
    Probable fix: add a type signature that fixes these type variable(s)
ghci> let vsum' = Variadic.foldl (+) 
ghci> :t vsum'
(Num a, Variadic a a r) => t -> a -> r
ghci> :t vsum' 0
(Num a, Variadic a a r) => a -> r
ghci> let V s = vsum' 0 1 2 3 in s
6

I'm guessing that's fallout from allowing UndecidableInstances, but I don't know, and I'd like to better understand what's going on.

解决方案

The idea behind functional dependencies is that in a declaration like

class Collapse a r | r -> a where
  ...

the r -> a bit says that a is uniquely determined by r. So, you can't have instance Collapse (a -> r) (a -> r) and instance Collapse a (a -> r). Note that instance Collapse (a -> r) (a -> r) follows from instance Collapse a a for the complete picture.

In other words, your code is trying to establish instance Collapse t t (the type variable's name is of course unimportant) and instance Collapse a (a -> r). If you substitute (a -> r) for t in the first instance declaration, you get instance Collapse (a -> r) (a -> r). Now this is the only instance of Collapse with the second type parameter equal to (a -> r) that you can have, because the class declaration says that the first type parameter is to be deducible from the second. Yet next you try to establish instance a (a -> r), which would add another instance of Collapse with the second type parameter being (a -> r). Thus, GHC complains.

这篇关于Haskell中的多变量函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆