Ram*_*eka 34 haskell clojure transducer
在阅读了介绍传感器的Clojure(http://blog.podsnap.com/ducers2.html)上的这篇文章之后,我对传感器的含义感到困惑.是否部分应用于map
Haskell,如map (+1)
传感器?起初我认为这是使用部分应用程序的Clojure方式,但随后本文继续在Haskell中使用显式类型实现它.它在Haskell中有什么用?
CR *_*ost 43
在Clojure中(map inc)
是一个传感器,但不在Haskell中,因为Haskell必须遵守currying,但是一个无类型的Lisp可以打破默认的咖喱惯例.换能器的类型是:
type Transducer a b = forall r . (b -> r -> r) -> (a -> r -> r)
Run Code Online (Sandbox Code Playgroud)
我们说,传感器"转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
Run Code Online (Sandbox Code Playgroud)
(我们也可以使用,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
Run Code Online (Sandbox Code Playgroud)
(如果你觉得提供这些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
Run Code Online (Sandbox Code Playgroud)
这将减少函数f
应用于a
且r
仅当谓词成立时.还有一个映射传感器:
tmap :: (a -> b) -> (b -> r -> r) -> a -> r -> r
tmap ba f a = f (ba a)
Run Code Online (Sandbox Code Playgroud)
这为任何"transducable"类型提供了可组合的map/filter语义:一个map/filter fn可以用于多个上下文.
传感器类型具有可爱的同构性:事实证明foldr
列表forall r. (x -> r -> r) -> r -> r
的完全等同于该列表[x]
(它是该列表的"教会编码"),因此将参数交换a
到传感器定义的最前面给了我们(IMO更容易理解!)类型type TransL a b = a -> [b]
.这更容易理解:
tl_map f = \a -> [f a]
tl_filter predicate = \a -> if predicate a then [a] else []
Run Code Online (Sandbox Code Playgroud)
要在列表上运行这些,请使用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合成运算符变得简单(.)
.