31

当我有一些类型的功能时

f :: (Ord a) => a -> a -> Bool
f a b = a > b

我想要用 not 包装这个函数的 make 函数。

例如制作这样的功能

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

4 回答 4

42

实际上,用类型类做任意的 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 (<)

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

于 2009-01-08T00:52:22.423 回答
41

除非你想对类型类进行修改,最好留给思想实验和概念证明,否则你不要泛化到多个参数。不要尝试。

至于你的主要问题,这是用 Conal Elliott 的语义编辑器组合器最优雅地解决的。语义编辑器组合器是一个具有如下类型的函数:

(a -> b) -> F(a) -> F(b)

哪里F(x)是一些涉及的表达式x。还有一些“逆变”编辑器组合器(b -> a)取而代之。直观地说,编辑器组合器选择某个较大值的一部分进行操作。您需要的称为result

result = (.)

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

a -> a -> Bool

这种类型的结果(codomain)是 ,该类型a -> Bool结果是,这就是您要应用的内容。因此,要应用于函数结果的结果,请编写:Boolnotnotf

(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]

而你想申请not布尔,你只需拼出到达那里的路径:

(result.left.result.second) not x

哦,如果你已经接触过 Functors,你会注意到它fmap是一个编辑器组合器。其实上面可以写成:

(fmap.left.fmap.fmap) not x

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

享受。

于 2009-01-06T01:54:17.697 回答
12

你的 n 组合器可以写成:

n = ((not .) .)

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

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

等等

于 2009-01-05T18:33:56.713 回答
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 的关于QuickCheck 的论文和 Ralf Hinze 的论文Generics for the Masses

于 2009-01-05T19:09:04.927 回答