在一个变构中组成f-代数的规则是什么?

Jog*_*ger 5 haskell recursion-schemes

这里有一些简单的F代数用于列表.他们与工作cata从函数 的递归的方案库.

import Data.Functor.Foldable

algFilterSmall :: ListF Int [Int] -> [Int]
algFilterSmall Nil = [] 
algFilterSmall (Cons x xs) = if x >= 10 then (x:xs) else xs

algFilterBig :: ListF Int [Int] -> [Int]
algFilterBig Nil = [] 
algFilterBig (Cons x xs) = if x < 100 then (x:xs) else xs

algDouble :: ListF Int [Int] -> [Int]
algDouble Nil = [] 
algDouble (Cons x xs) = 2*x : xs

algTripple :: ListF Int [Int] -> [Int]
algTripple Nil = [] 
algTripple (Cons x xs) = 3*x : xs
Run Code Online (Sandbox Code Playgroud)

现在我把它们组成:

doubleAndTripple :: [Int] -> [Int]
doubleAndTripple = cata $ algTripple . project . algDouble
-- >>> doubleAndTripple [200,300,20,30,2,3]
-- [1200,1800,120,180,12,18]
Run Code Online (Sandbox Code Playgroud)

doubleAndTriple按预期工作.两个代数都是结构保留的,它们不会改变列表的长度,因此cata可以将两个代数应用于列表的每个项目.

下一个是过滤和双重:

filterAndDouble :: [Int] -> [Int] 
filterAndDouble = cata $ algDouble . project . algFilterBig
-- >>> filterAndDouble [200,300,20,30,2,3]
-- [160,60,4,6]
Run Code Online (Sandbox Code Playgroud)

它无法正常工作.我认为这是因为algFilterBig不是结构保留.

现在是最后一个例子:

filterBoth :: [Int] -> [Int] 
filterBoth = cata $ algFilterSmall . project . algFilterBig 
-- >>> filterBoth [200,300,20,30,2,3]
-- [20,30]
Run Code Online (Sandbox Code Playgroud)

这两个代数都不是结构保留,但这个例子是有效的.

组成f-algebras的确切规则是什么?

Li-*_*Xia 4

“结构保持代数”可以形式化为自然变换(可以在不同函子之间)。

inList :: ListF a [a] -> [a]
inList Nil = []
inList (Cons a as) = a : as

ntDouble, ntTriple :: forall a. ListF Int a -> ListF Int a
algDouble = inList . ntDouble
algTriple = inList . ntTriple
Run Code Online (Sandbox Code Playgroud)

那么,对于任何代数f和自然变换n

cata (f . inList . n) = cata f . cata n
Run Code Online (Sandbox Code Playgroud)

该示例是和doubleAndTriple的一个实例。f = algTriplen = ntDouble

这不容易推广到更大的代数类。

我认为过滤器的情况在没有类别的情况下更容易看到,因为它filter是半群同态:filter p . filter q = filter (liftA2 (&&) p q)


我寻找类似过滤代数的“分配律”的一般但充分的条件。稍微缩写一下afs = algFilterSmallafb = algFilterBig。我们向后推理,找到以下充分条件:

cata (afs . project . afb) = cata afs . cata afb  -- Equation (0)
Run Code Online (Sandbox Code Playgroud)

根据变质论的定义,z = cata (afs . project . afb)是该方程的唯一解(伪装的交换图):

z . inList = afs . project . afb . fmap z
Run Code Online (Sandbox Code Playgroud)

代入z上式的 RHS:

cata afs . cata afb . inList = afs . project . afb . fmap (cata afs . cata afb)
-- (1), equivalent to (0)
Run Code Online (Sandbox Code Playgroud)
  • 在 LHS 上,我们将 的定义应用cata为 Haskell 函数 ,cata afb = afb . fmap (cata afb) . project并用 进行简化project . inList = id

  • 在 RHS 上,我们应用函子定律fmap (f . g) = fmap f . fmap g

这产生:

cata afs . afb . fmap (cata afb) = afs . project . afb . fmap (cata afs) . fmap (cata afb)
-- (2), equivalent to (1)
Run Code Online (Sandbox Code Playgroud)

我们“简化”掉最后一个因素fmap (cata afb)(请记住,我们正在向后推理):

cata afs . afb = afs . project . afb . fmap (cata afs)  -- (3), implies (2)
Run Code Online (Sandbox Code Playgroud)

这是我能想到的最简单的一个。

  • 在 Haskell 中,“filter”实际上是对偶半群的同态:“filter p”。过滤器 q = 过滤器 (liftA2 (&amp;&amp;) qp)`。特别是 `filter (const undefined) 。过滤器(const False)=过滤器(const False)`。显然,未能交换这些导致了 GHC 早期“过滤器”重写规则中的错误! (3认同)