我正在阅读the original paper about data types a la carte,并决定尝试在Scala中实现该想法(我知道它已经在许多功能库中实现)。不幸的是,我发现原始论文难以理解,一开始我就停留在某个地方。然后,我发现another paper更易于理解,并且设法将论文中的Haskell代码重写为Scala,您可以找到here。但是我仍然在努力地理解一下:
原始Expr
数据类型
data Expr = Val Int | Add Expr Expr
新型签名:
data Arith e = Val Int | Add e e
对于任何函子
f
,其诱导的递归数据类型Fix f
被定义为f
的最小固定点,实现如下:
data Fix f = In (f (Fix f))
现在我们已经绑上签名的递归结,
Fix Arith
是与原始Expr
数据类型等效的语言 允许整数值和加法运算。
“我们已经绑定了签名的递归结”到底是什么意思?Fix Arith
是与原始Expr
等效的语言吗?
In
的实际类型是In :: f (Fix f) -> Fix f
如果我们尝试使用In
构造和Val 1
变量构造一个值,我们将得到以下结果:
> :t In(Val 1)
> In(Val 1) :: Fix Arith
相同数据类型的标量编码:
sealed trait Arith[A]
case class Val[A](x: Int) extends Arith[A]
case class Add[A](a: A, b: A) extends Arith[A]
trait Fix[F[_]]
case class In[F[_]](exp: F[Fix[F]]) extends Fix[F]
fold
函数
fold
函数具有以下签名和实现Haskell:
fold :: Functor f => (f a -> a) -> Fix f -> a
fold f (In t) = f (fmap (fold f) t)
我想出的标量变体
def fold[F[_] : Functor, A](f: F[A] => A): Fix[F] => A = {
case In(t) =>
val g: F[Fix[F]] => F[A] = implicitly[Functor[F]].lift(fold(f))
f(g(t))
}
我很好奇的是,在我的Scala版本中,函数g
的类型为F[Fix[F]] => F[A]
,但是模式匹配后变量t
的类型为{{1} }的值为LaCarte$Add
,如何将函数Add(In(Val(1)),In(Val(2)))
应用于g
是有效的?另外,如果您能帮助我理解LaCarte$Add
函数,我将不胜感激?
引文:
fold的第一个参数是 f-代数,它提供 与给定签名
fold
关联的每个构造函数的行为。
答案 0 :(得分:2)
“我们已经绑定了签名的'递归结'”到底是什么意思?
原始的Expr
数据类型是递归的,以其自己的定义引用自身:
data Expr = Val Int | Add Expr Expr
Arith
类型通过用参数替换递归调用来“递归”递归:
data Arith e = Val Int | Add e e
原始Expr
类型可以具有任何嵌套深度,我们也希望Arith
支持这种嵌套,但是最大深度取决于我们为e
选择的类型:< / p>
Arith Void
无法嵌套:它只能是文字值(Val n
),因为我们无法构造Add
,因为我们无法获得类型为Void
(没有构造函数)的值
Arith (Arith Void)
可以具有一层嵌套:外部构造函数可以是Add
,而内部构造函数只能是Lit
。
Arith (Arith (Arith Void))
可以具有两个级别
以此类推
Fix Arith
给我们的是一种谈论不动点 Arith (Arith (Arith …))
的方式,对深度没有限制。
这就像我们可以用非递归函数替换递归函数并使用定点组合器恢复递归一样:
factorial' :: (Integer -> Integer) -> Integer -> Integer
factorial' recur n = if n <= 1 then 1 else n * recur (n - 1)
factorial :: Integer -> Integer
factorial = fix factorial'
factorial 5 == 120
Fix Arith
是与原始Expr
等效的语言吗?
Fix Arith
表示的语言(语法)与Expr
表示的语言等效;也就是说,它们是同构的:您可以编写一对总函数Fix Arith -> Expr
和Expr -> Fix Arith
。
将功能
g
应用于LaCarte$Add
会如何有效?
我对Scala不太熟悉,但是看起来Add
是Arith
的子类型,因此可以填充类型g
的{{1}}的参数的类型为F[Fix[F]]
的值,您可以通过在Arith[Fix[Arith]]
构造函数上进行匹配来“展开”一级递归。