我目前正在阅读 John Hughes 的论文Programming with Arrows,我已经被第 20 页第 2.5 节的第一个练习难住了。
我们可以使用Arrow
和类型类,以及通过类型的函数、流函数和单子函数的ArrowChoice
实例。[a] -> [b]
a -> m b
Kleisli
举了一个mapA
例子:
mapA f = arr listcase >>>
arr (const []) ||| (f *** mapA >>> arr (uncurry (:)))
这是一个尝试:
listcase :: [a] -> (Either () (a,[a]))
listcase [] = Left ()
listcase (x:xs) = Right (x,xs)
helper :: (Bool,a) -> [a] -> Either (a,[a]) [a]
helper (True,x) y = Left (x,y)
helper (False,x) y = Right y
test :: Arrow a => (b -> Bool) -> a (b,c) ((Bool,b), c)
test p = first (arr p &&& arr id)
filterA :: Arrow a => (b -> Bool) -> a [b] [c]
filterA p = f >>> (g ||| (h >>> (j ||| (filterA p))))
where f = arr listcase
g = arr (const [])
h = test p >>> (uncurry helper)
j = (arr id *** (filterA p)) >>> (arr (uncurry (:)))
这种徒劳的尝试背后的(蛮力)基本原理如下:有两种选择filterA
:listcase
likemap
和应用 predicate 的结果p
。它开始于map
,检查列表并使用返回一个 Either 值listcase
。在应用空列表的情况下g
,否则右侧的所有内容|||
都应用于 type 的值,分别(a,[a])
包含head
和tail
。该h
函数首先应用,它应用谓词同时保留head
,返回一个类型的值((Bool, head),tail)
。这被传递给,它根据值(uncurry helper)
决定是否保留。它将结果作为head
Bool
Either
值,以便我们可以将选择方法(|||)
应用于它。这个值被传递给下一个选择:(j ||| (filterA p))
这样,如果谓词被持有True
thenj
将应用于包含head
and的对tail
。被head
过滤,id
而filter p
被应用于tail
。两个结果都成对返回。arr (uncurry (:))
然后使用like协调这对map
。否则,将单独tail
传递给。filterA p
我怀疑它是否像我想象的那样困难,我想我错过了一些非常明显的东西。