如何用任意arity函数组合`not`?

时间:2009-01-05 17:54:04

标签: function haskell functional-programming composition

当我有类似

类型的功能时
f :: (Ord a) => a -> a -> Bool
f a b = a > b

我想用make函数包装这个函数。

e.g。像这样做函数

g :: (Ord a) => a -> a -> Bool
g a b = not $ f a b

我可以像

一样制作组合器
n f = (\a -> \b -> not $ f a b)

但我不知道如何。

*Main> let n f = (\a -> \b -> not $ f a b)
n :: (t -> t1 -> Bool) -> t -> t1 -> Bool
Main> :t n f
n f :: (Ord t) => t -> t -> Bool
*Main> let g = n f
g :: () -> () -> Bool

我做错了什么?

还有一个问题是如何通过更多和更少的参数来实现这一功能,例如

t -> Bool
t -> t1 -> Bool
t -> t1 -> t2 -> Bool
t -> t1 -> t2 -> t3 -> Bool

4 个答案:

答案 0 :(得分:40)

除非您想要使用类型类别进行黑客攻击,这最好留给思想实验和概念证明,否则您只需要推广到多个参数。不要试试。

至于你的主要问题,这是Conal Elliott的语义编辑器组合器最优雅的解决方案。语义编辑器组合器是一种类型为:

的函数
(a -> b) -> F(a) -> F(b)

其中F(x)是涉及x的表达式。还有“逆变”编辑器组合,它取代(b -> a)。直观地说,编辑器组合器选择一些较大值的一部分来操作。您需要的名为result

result = (.)

查看您尝试操作的表达式的类型:

a -> a -> Bool

此类型的结果(codomain)为a -> Bool 类型的结果为Bool,这就是您尝试应用的结果{{1 }} 至。因此,要将not应用于函数not的结果,请写下:

f

这美妙地概括。以下是一些组合器:

(result.result) not f

因此,如果您的值为argument = flip (.) -- contravariant first f (a,b) = (f a, b) second f (a,b) = (a, f b) left f (Left x) = Left (f x) left f (Right x) = Right x ... ,则类型为:

x

你想将Int -> Either (String -> (Int, Bool)) [Int] 应用于Bool,你只需说出去那里的路径:

not

哦,如果你还有Functors,你会注意到(result.left.result.second) not x 是一个编辑组合。事实上,上面可以拼写:

fmap

但我认为使用扩展名称更清楚。

享受。

答案 1 :(得分:40)

实际上,使用类型类做任意arity变得异常简单:

module Pred where

class Predicate a where
  complement :: a -> a

instance Predicate Bool where
  complement = not

instance (Predicate b) => Predicate (a -> b) where
  complement f = \a -> complement (f a)  
  -- if you want to be mysterious, then
  -- complement = (complement .)
  -- also works

ge :: Ord a => a -> a -> Bool
ge = complement (<)

感谢您指出这个很酷的问题。我爱哈斯克尔。

答案 2 :(得分:12)

你的n个组合子可以写成:

n = ((not .) .)

至于你的奖金问题,典型的方法是创建其中的几个:

lift2 = (.).(.)
lift3 = (.).(.).(.)
lift4 = (.).(.).(.).(.)
lift5 = (.).(.).(.).(.).(.)

答案 3 :(得分:9)

回复:我做错了什么?

我认为你的组合器很好,但是当你把它绑定在顶层时,Haskell的一个令人讨厌的“默认规则”就会发挥作用而且绑定不是一般的:

Prelude> :ty (n f)
(n f) :: (Ord t) => t -> t -> Bool
Prelude> let g = n f
Prelude> :ty g
g :: () -> () -> Bool

我认为你可能会受到'单态限制'的影响,因为它适用于类型类。在任何情况下,如果你离开顶级循环并将事物放入具有显式类型签名的单独文件中,那么一切正常:

module X where

n f = (\a -> \b -> not $ f a b)
f a b = a > b

g :: Ord a => a -> a -> Bool
g = n f

奖金问题:要使用越来越多的类型参数执行此操作,您可以尝试使用类型系统播放坏血病技巧。要咨询的两篇论文是Hughes和Claessen的paper on QuickCheck以及Ralf Hinze的论文Generics for the Masses