import Control.Applicative
import Control.Arrow
filter ((&&) <$> (>2) <*> (<7)) [1..10]
filter ((>2) &&& (<7) >>> uncurry (&&)) [1..10]
Run Code Online (Sandbox Code Playgroud)
两者都得到相同的结果!但是,我很难理解.有人可以在这里详细解释一下吗?
C. *_*ann 23
让我们从第二个开始,这更简单.我们这里有两个神秘的操作符,具有以下类型:
(&&&) :: Arrow a => a b c -> a b c' -> a b (c, c')
(>>>) :: Category cat => cat a b -> cat b c -> cat a c
Run Code Online (Sandbox Code Playgroud)
该Arrow和Category类型类大多是有关的事情,表现得像功能,其中当然也包括函数本身,而且这里两个实例都只是普通的(->).所以,重写要使用的类型:
(&&&) :: (b -> c) -> (b -> c') -> (b -> (c, c'))
(>>>) :: (a -> b) -> (b -> c) -> (a -> c)
Run Code Online (Sandbox Code Playgroud)
第二个类型(.)与熟悉的函数组合运算符非常相似; 事实上,它们是相同的,只是交换了参数.第一个是更陌生的,但类型再次告诉你所有你需要知道的 - 它需要两个函数,都采用一个普通类型的参数,并产生一个函数,将两者的结果组合成一个元组.
因此,表达式(>2) &&& (<7)采用单个数字并Bool根据比较生成一对值.然后将其结果输入,这只uncurry (&&)需要一对Bools和AND将它们组合在一起.结果谓词用于以通常方式过滤列表.
第一个更神秘.我们有两个神秘的运算符,具有以下类型:
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)
观察(<$>)在这种情况下的第二个参数是(>2),哪个有类型(Ord a, Num a) => a -> Bool,而(<$>)'s参数的类型有类型f a.这些如何兼容?
答案是,正如我们可以替代(->)的a,并cat在前面的类型签名,我们可以想到的a -> Bool是(->) a Bool,和替代((->) a)的f.因此,重写类型,((->) t)改为使用以避免与其他类型变量冲突a:
(<$>) :: (a -> b) -> ((->) t) a -> ((->) t) b
(<*>) :: ((->) t) (a -> b) -> ((->) t) a -> ((->) t) b
Run Code Online (Sandbox Code Playgroud)
现在,把东西放回正常的中缀形式:
(<$>) :: (a -> b) -> (t -> a) -> (t -> b)
(<*>) :: (t -> (a -> b)) -> (t -> a) -> (t -> b)
Run Code Online (Sandbox Code Playgroud)
第一个结果是函数组合,你可以从类型中观察到.第二个更复杂,但是再次类型告诉你你需要什么 - 它需要两个函数,一个是普通类型的参数,一个产生一个函数,另一个产生一个参数传递给函数.换句话说,就像\f g x -> f x (g x).(这个函数也恰好被称为组合逻辑中的S组合子,逻辑学家Haskell Curry广泛探讨了这个主题,其名称无疑似乎很奇怪!)
单独做的"扩展" 的组合(<$>)和(<*>)扩展(<$>),在这种情况下,意味着使用两个参数的函数,两个具有公共参数类型的函数,将单个值应用于后两个,然后将第一个函数应用于两个结果.因此((&&) <$> (>2) <*> (<7)) x简化为(&&) ((>2) x) ((<7) x)或使用正常的中缀风格x > 2 && x < 7.和以前一样,复合表达式用于以通常的方式过滤列表.
另外,请注意,虽然这两个函数在某种程度上都是混淆的,但是一旦习惯了所使用的运算符,它们实际上就具有很强的可读性.第一个抽象复合表达式对单个参数执行多个操作,而第二个抽象是标准"管道"样式的一般形式,将事物与函数组合串联起来.
就个人而言,我实际上发现第一个完全可读.但我不希望大多数人同意!
| 归档时间: |
|
| 查看次数: |
463 次 |
| 最近记录: |