我想这取决于您所说的“有意义的解释”是什么意思。
Ifs
是递归数据类型和核心递归余数据类型的基本函子,如以下s ~ ListF e
递归列表数据类型的函子[e]
(在 Haskell 中,它也是核心递归流余数据类型):
{-# LANGUAGE DeriveFunctor #-}
data ListF e b = Nil | Cons e b deriving (Show, Functor)
然后一个s
类型的 -coalgebraa -> s a
和一个起始种子可以通过从该种子展开a
来生成一个 codata 类型的值,而一个类型的-algebra可以通过折叠成一个类型的值来消耗一个数据类型的值。该函数只是结合了展开和折叠到的操作,而没有实际创建中间 codata/data 类型。[e]
s
s b -> b
[e]
b
refold
a
b
例如,您可以通过使用起始值/余代数对[10,9..1]
从种子展开来生成(有限)余数据流,如下所示:Integer
(a,g)
a :: Integer
a = 10
g :: Integer -> (ListF Integer) Integer
g 0 = Nil
g n = Cons n (n-1)
并折叠一个列表以Int
使用代数计算其长度:
f :: (ListF Integer) Int -> Int
f Nil = 0
f (Cons _ b) = 1 + b
该refold
函数只是结合了这些操作:
main = print $ refold f g a
在这种特殊情况下,它计算10
流/列表的长度[1..10]
而不实际创建任何中间流/列表。
我猜直觉是,如果一个操作可以被想象成一个 F 递归应用于同一个函子 F 的 F 核递归,那么它就是一个refold
. 或者,也许更实际地,如果一个算法有一个与函子 F 匹配的内部递归结构,它可以表示为 a refold
。in的文档给出了快速排序的示例,该示例具有与二叉树匹配的递归结构,尽管您可能已经看过该示例。refold
recursion-schemes
注意:下面的内容是错误的或充其量是不精确的,但我会尝试多考虑一下。
在实践中,refold
它不仅用作通用数据类型之间的态射,而且如果您有与函子关联的余数据类型的最终s-coalgebra :C
s
eatC :: C -> ListF Integer C
以及与仿函数相关的数据类型的初始s 代数:D
s
makeD :: ListF Integer D -> D
那么refold makeD eatC
应该是从 codata 类型C
到数据类型的自然态射D
。也就是说,它应该是满足的唯一态射:
fmap h . refold makeD eatC = refold makeD eatC . fmap h
我不确定这方面是否非常有趣......