将haskel中的所有函数参数作为列表

时间:2018-02-23 13:46:35

标签: haskell

haskell是否有办法将所有函数参数作为列表。

让我们来看看我们有以下程序,我们想要添加两个较小的数字,然后减去最大的数字。假设,我们无法更改foo :: Int -> Int -> Int -> Int的函数定义。有没有办法将所有函数参数作为列表获取,而不是构造一个新列表并将所有参数作为所述列表的元素添加?更重要的是,是否存在一种独立于参数数量的一般方法?

示例:

module Foo where
    import Data.List

    foo :: Int -> Int -> Int -> Int
    foo a b c = result!!0 + result!!1 - result!!2 where result = sort ([a, b, c])

2 个答案:

答案 0 :(得分:6)

  

有没有一种通用的方法来做这个独立于参数的数量?

不是真的;至少它不值得。首先,这个整个想法并不是非常有用,因为列表是同类:所有元素必须具有相同的类型,所以它只适用于相当不寻常的函数的特殊情况单一类型。

即便如此,问题在于“参数数量”在Haskell中并不是一个明智的概念,因为正如Willem Van Onsem评论的那样,所有函数实际上只有一个参数(进一步参数实际上只给第一个应用程序的结果,它还具有函数类型。)

也就是说,至少对于单个参数和最终结果类型,将任意数量的参数打包到列表中是非常容易的:

{-# LANGUAGE FlexibleInstances         #-}

class UsingList f where
  usingList :: ([Int] -> Int) -> f

instance UsingList Int where
  usingList f = f []

instance UsingList r => UsingList (Int -> r) where
  usingList f a = usingList (f . (a:))

foo :: Int -> Int -> Int -> Int
foo = usingList $ (\[α,β,γ] -> α + β - γ) . sort

使用类型族或多参数类型类,也可以使任何类型的参数都能使用。然而,不是那么简单的是用最终结果的可变类型一劳永逸地写出它。原因是,它还必须处理函数作为最终结果的类型。但是,这也可以解释为“我们仍然需要在列表中再添加一个参数”!

答案 1 :(得分:1)

尽管如此,我还是不同意@leftaroundabout's answer above。有事 不寻常的不是将其视为不值得的理由。

您无法定义多态可变参数列表构造函数是正确的 没有类型注释。但是,我们通常不会处理类型为Haskell 98的问题 从来没有要求注释。有Dependent Haskell指日可待 熟悉非平凡类型的注释变得至关重要。

所以,让我们来看看这个,无视值得考虑。

定义似乎不允许单一类型的函数的一种方法是使其成为a的方法 适当建造的课程。很多涉及类型类的技巧都是由狡猾设计的 Haskellers,至少早在15 years ago开始。即使我们不理解他们的 在所有深度的类型巫术,我们仍然可以尝试类似的方法。

让我们首先尝试获得一种求和任意数量整数的方法。这意味着反复 应用像(+)这样的函数,使用统一类型,例如a -> a -> a。这是一种方法 它:

class Eval a where
    eval :: Integer -> a

instance (Eval a) => Eval (Integer -> a) where
    eval i = \y -> eval (i + y)

instance Eval Integer where
    eval i = i

这是repl的摘录:

λ eval 1 2 3 :: Integer
6

请注意,我们不能没有明确的类型注释,因为我们的方法的想法是 表达式eval x1 ... xn可能是一个等待另一个参数的函数, 或最终价值。

现在一个概括是实际制作一个值列表。科学tells us那个 我们可以从列表中派生出任何monoid。事实上,只要总和是一个幺半群,我们可以将论据转向 列表,然后求和,得到与上面相同的结果。

以下是我们如何将我们方法的参数转换为列表:

class Eval a where
    eval2 :: [Integer] -> Integer -> a

instance (Eval a) => Eval (Integer -> a) where
    eval2 is i = \j -> eval2 (i:is) j

instance Eval [Integer] where
    eval2 is i = i:is

这是如何运作的:

λ eval2 [] 1 2 3 4 5 :: [Integer]
[5,4,3,2,1]

不幸的是,我们必须使eval二进制,而不是一元,因为它现在必须组成两个 不同的东西:一个(可能是空的)值列表和下一个值。请注意它是怎样的 类似于通常的foldr

λ foldr (:) [] [1,2,3,4,5]
[1,2,3,4,5]

我们想要的下一个概括是允许列表中的任意类型。这有点儿 棘手,因为我们必须使Eval成为一个2参数类型类:

class Eval a i where
    eval2 :: [i] -> i -> a

instance (Eval a i) => Eval (i -> a) i where
    eval2 is i = \j -> eval2 (i:is) j

instance Eval [i] i where
    eval2 is i = i:is

它与Integer的上一个一样,但它也可以携带任何其他类型,甚至是函数:

(我很抱歉这个凌乱的例子。我不得不以某种方式show一个函数。)

λ ($ 10) <$> (eval2 [] (+1) (subtract 2) (*3) (^4) :: [Integer -> Integer])
[10000,30,8,11]

到目前为止一切顺利:我们可以将任意数量的参数转换为列表。但是,这很难 将该函数与对结果列表有用的函数组合起来,因为 组合只承认一元函数 - 有一些技巧,二进制函数,但绝不是 可变参数。好像我们必须定义自己的组合函数的方法。这就是我的看法:

class Ap a i r where
    apply :: ([i] -> r) -> [i] -> i -> a
    apply', ($...) :: ([i] -> r) -> i -> a
    ($...) = apply'

instance Ap a i r => Ap (i -> a) i r where
    apply f xs x = \y -> apply f (x:xs) y
    apply' f x = \y -> apply f [x] y

instance Ap r i r where
    apply f xs x = f $ x:xs
    apply' f x = f [x]

现在我们可以将所需的函数编写为任何数字的列表允许函数的应用程序 参数:

foo' :: (Num r, Ord r, Ap a r r) => r -> a
foo' = (g $...)
    where f = (\result -> (result !! 0) + (result !! 1) - (result !! 2))
          g = f . sort

您仍然需要在每个呼叫站点上键入注释,如下所示:

λ foo' 4 5 10 :: Integer
-1

- 但到目前为止,这是我能做的最好的事情。

我学习Haskell越多,我就越确定没有什么是不可能的。