2011-06-09 30 views
11
import Control.Applicative 
import Control.Arrow 

filter ((&&) <$> (>2) <*> (<7)) [1..10] 
filter ((>2) &&& (<7) >>> uncurry (&&)) [1..10] 

两者都得到相同的结果!但是,我很难理解。这里有人能详细解释吗?你介意在论坛中解释代码吗?

+0

这些例子来自哪里? – 2011-06-09 03:33:57

+0

应用程序确实需要自己的语法糖,适用于普通应用程序和中缀应用程序。 '过滤器((> 2)<(&&)>(<7))[1..10]'会很好地读取。 – 2011-06-09 03:44:44

+0

@pelotom:总会有成语括号。我们现在有monad理解,为什么不能应用理解? :) – 2011-06-09 03:58:24

回答

23

让我们从第二个开始,这更简单。我们有两个神秘的运营商在这里,有以下几种类型:

(&&&) :: Arrow a => a b c -> a b c' -> a b (c, c') 
(>>>) :: Category cat => cat a b -> cat b c -> cat a c 

ArrowCategory型类主要是对的事情,表现得像功能,其中当然也包括函数本身,而且这里两个实例都只是普通的(->)。所以,重写类型以使用:

(&&&) :: (b -> c) -> (b -> c') -> (b -> (c, c')) 
(>>>) :: (a -> b) -> (b -> c) -> (a -> c) 

第二具有非常相似类型(.),熟悉的功能组合物算子;实际上,它们是相同的,只是换了参数。第一种是更不熟悉的,但是这些类型再次告诉你所有你需要知道的东西 - 它有两个函数,它们接受一个通用类型的参数,并且产生一个单独的函数,将这两个函数结合成一个元组。

因此,表达式(>2) &&& (<7)采用单个号码,并产生一个对基于所述比较Bool值。然后将这个结果输入到uncurry (&&),它只需要一对Bool s和与它们在一起。得到的谓词用于以通常的方式过滤列表。


第一个比较神秘。我们有两个神秘的运营商,再次,有以下类型:

(<$>) :: Functor f => (a -> b) -> f a -> f b 
(<*>) :: Applicative f => f (a -> b) -> f a -> f b 

观察到的(<$>)在这种情况下,第二个参数是(>2),其类型为(Ord a, Num a) => a -> Bool,而(<$>)的参数的类型有类型f a。这些如何兼容?

答案是,正如我们可以在前面的类型签名代替(->)acat,我们能想到的a -> Bool(->) a Bool,并替换((->) a)f。所以,重写类型,使用((->) t)来避免与其他类型的变量a冲突:

(<$>) :: (a -> b) -> ((->) t) a -> ((->) t) b 
(<*>) :: ((->) t) (a -> b) -> ((->) t) a -> ((->) t) b 

现在,把东西放回正常缀形式:

(<$>) :: (a -> b) -> (t -> a) -> (t -> b) 
(<*>) :: (t -> (a -> b)) -> (t -> a) -> (t -> b) 

第一个结果是功能组成,你可以从类型中观察。第二个更复杂,但是再一次,这些类型告诉你你需要什么 - 它带有两个带有通用类型参数的函数,一个产生函数,另一个产生一个参数传递给函数。换句话说,就像\f g x -> f x (g x)。 (这个功能也恰好被称为S combinator组合逻辑,这个主题由逻辑学家Haskell Curry广泛探讨,其名字无疑似乎很奇怪!)

(<$>)组合和(<*>)排序的“延伸”,单独什么(<$>)确实,在这种情况下,意味着拍摄功能有两个参数,两个函数具有共同参数类型,将单个值后两者,然后将第一个函数应用于两个结果。所以((&&) <$> (>2) <*> (<7)) x简化为(&&) ((>2) x) ((<7) x),或使用正常中缀风格,x > 2 && x < 7。和以前一样,复合表达式用于以通常的方式过滤列表。


此外,注意,虽然这两种功能会被混淆到一定程度,一旦你习惯使用的运营商,它们实际上是相当的可读性。第一个摘要将复合表达式用于多个事物的单个参数,而第二个是标准的“管道”风格的串联函数组合的标准“管道”风格的一般形式。

就我个人而言,我实际上找到第一个完全可读的。但我不希望大多数人同意!

+0

感谢您的回答!我让我很清楚! – 2011-06-09 05:14:05

+0

+1:你比我更好地解释了我的代码。 – Landei 2011-06-09 06:53:04

+3

我会补充说,哈斯克勒不会每次都在头上演示这种类型的体操。相反,模式:'f <$> x <*> y'(也拼写:'liftA2 f x y')是众所周知的。这意味着:在两个Applicative-wrapped值上应用函数f。函数可以被看作是“包装值”:'(> 2)'和'(<7)'可以被看作是包装Bools,其中'Num a =>(( - >)a)'是“包装器”。函数'(&&)'因此可以应用在两个“包装”布尔上以产生结果包装布尔。同样可以用来定义:'avg = liftA2(/)总长度' – Peaker 2011-06-10 01:07:53