在阅读了关于Clojure(http://blog.podsnap.com/ducers2.html)介绍传感器的这篇文章后,我对传感器的含义感到困惑。是否在Haskell中部分应用map
,例如map (+1)
传感器?起初我认为这是使用部分应用程序的Clojure方式,但随后本文继续在Haskell中使用显式类型实现它。它在Haskell中有什么用处?
答案 0 :(得分:41)
在Clojure中,(map inc)
是一个传感器,但不在Haskell中,因为Haskell必须服从currying,但是一个无类型的Lisp可以打破默认的curry惯例。换能器的类型是:
type Transducer a b = forall r . (b -> r -> r) -> (a -> r -> r)
我们说换能器'将a
变成b
'。是的,a
和b
似乎是“向后”的右侧。 forall
表示传感器必须将r
作为常规类型变量,但完全允许专注于a
和b
。
让我在foldr中反转两个参数:
-- cf. with foldr :: (x -> r -> r) -> r -> [x] -> r
ffoldr :: (x -> r -> r) -> [x] -> r -> r
ffoldr = flip . foldr
(我们也可以使用foldl
,但以后会倾向于反转)。这意味着可以使用Transducer
将ffoldr
的第一个参数从x
转换为y
,这样我们就可以使用[x]
来处理y -> r -> r
foldr
使用([x], r)
。传感器位于输入(y, r) -> r
和最终处理器ffoldr :: Transducer [x] x
之间。
我翻开了后两个论点,也强调了(.) :: Transducer a b -> Transducer b c -> Transducer a c
。通过使用参数的对称性,我们还有一个通用的传感器组合,恰好只是函数组合:
forall r
(如果您认为提供这些.
条款让我们反转您通常使用tfilter :: (a -> Bool) -> (a -> r -> r) -> a -> r -> r
-- or: (a -> Bool) -> Transducer a a
tfilter predicate f a = if predicate a then f a else id
的方式很酷,您可以通过一种名为“延续传递”的技术随意执行。)
例如,这里是滤波器传感器:
f
仅当谓词成立时,这会将缩减函数a
应用于r
和tmap :: (a -> b) -> (b -> r -> r) -> a -> r -> r
tmap ba f a = f (ba a)
。还有一个映射传感器:
foldr
这为任何“transducable”类型提供了可组合的map / filter语义:一个map / filter fn可以用于多个上下文。
传感器类型具有可爱的同构性:事实证明,列表forall r. (x -> r -> r) -> r -> r
的{{1}}完全等同于列表[x]
(它是“教会编码”) list),因此将参数a
交换到传感器定义的最前面使我们(IMO更容易理解!)类型type TransL a b = a -> [b]
。这更容易理解:
tl_map f = \a -> [f a]
tl_filter predicate = \a -> if predicate a then [a] else []
要在列表中运行这些,请使用concatMap
...恰好是>>=
!所以你只需要写collection >>= transducer
并获得转换后的集合。 TransL a b
的含义是,“取a
原始列表的每个元素,并给我0个或更多类型b
的元素拼接到我的传出列表中。”当谓词不起作用时,它通过拼接0个元素进行过滤;它通过为每个输入元素产生1个输出元素进行映射;另一个操作tl_dupe = \a -> [a, a]
是一个传感器,它复制列表中的元素,[1,2,3] >>= tl_dupe
变为[1,1,2,2,3,3]
。
foldr
似乎是Transducer [x] x
,现在看来它与id :: TransL [x] x
完全相同,后者可以在中间执行concat
操作计算;这个代数中的身份函数实际上是return = \a -> [a]
,也写成(:[])
。 唯一损失是我们无法再使用.
来构建这些内容,但实际上Control.Monad
中提供的内容与Kleisli合成运算符>=>
相同
如此长的故事简短,换能器是函数a -> [b]
巧妙地转换为一些教会编码,以便列表monad的这些Kleisli箭头的Kleisli合成运算符变为简单(.)
。