我目前正在阅读 John Hughes 的论文Programming with Arrows,我已经被第 20 页第 2.5 节的第一个练习难住了。
我们可以使用Arrow和类型类,以及通过类型的函数、流函数和单子函数的ArrowChoice实例。[a] -> [b]a -> m bKleisli
举了一个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:listcaselikemap和应用 predicate 的结果p。它开始于map,检查列表并使用返回一个 Either 值listcase。在应用空列表的情况下g,否则右侧的所有内容|||都应用于 type 的值,分别(a,[a])包含head和tail。该h函数首先应用,它应用谓词同时保留head,返回一个类型的值((Bool, head),tail)。这被传递给,它根据值(uncurry helper)决定是否保留。它将结果作为headBoolEither值,以便我们可以将选择方法(|||)应用于它。这个值被传递给下一个选择:(j ||| (filterA p))这样,如果谓词被持有Truethenj将应用于包含headand的对tail。被head过滤,id而filter p被应用于tail。两个结果都成对返回。arr (uncurry (:))然后使用like协调这对map。否则,将单独tail传递给。filterA p
我怀疑它是否像我想象的那样困难,我想我错过了一些非常明显的东西。