最近我一直在试验一般性问题, GHC允许我做什么?我很惊讶地发现,它认为以下程序是有效的
module BrokenRecursiveType where
data FooType = Foo FooType
main = print "it compiles!"
起初我想,这有什么用?然后我记得Haskell很懒,所以我也许可以定义一个像下面这样的函数来使用它
allTheFoos = Foo allTheFoos
然后我想,所以这有用吗?
对于FooType
类似表单的类型,是否有任何有价值的用例(想出或实际经历过)?
答案 0 :(得分:9)
假设您可以使用FooType
来提前中止递归函数:例如,请使用以下代码:
foo _ 0 = 1
foo (Foo x) n = n * foo x (n-1)
如果您致电foo allTheFoos
,那么您将获得简单的阶乘功能。但是您可以传递FooType
类型的不同值,例如
atMostFiveSteps = Foo (Foo (Foo (Foo (Foo (error "out of steps")))))
然后foo atMostFiveSteps
仅适用于小于6的值。
我不是说这是特别有用的,也不是说这是实现这样一个功能的最好方法......
newtype FooType' = Foo' FooType'
这很有用:这是定义除了⊥之外没有值的void类型的一种方法。你仍然可以定义
allTheFoos' = Foo' allTheFoos'
和以前一样,但由于操作上Foo
什么也没做,这相当于x = x
,因此也是⊥。
答案 1 :(得分:4)
让我们稍微扩展您的数据类型 - 让我们将递归包装到类型参数中:
data FooType f = Foo (f (FooType f))
(因此您的原始数据类型为FooType Identity
)。
现在我们可以通过任何f :: * -> *
来调制递归引用。但这种扩展类型非常有用!实际上,它可以用于表示使用非递归数据类型的任何递归数据类型。其中定义的一个kwnown包是 recursion-schemes ,如Fix
:
newtype Fix f = Fix (f (Fix f))
例如,如果我们定义
data List' a r = Cons' a r | Nil'
然后Fix (List' a)
与[a]
同构:
nil :: Fix (List' a)
nil = Fix Nil'
cons :: a -> Fix (List' a) -> Fix (List' a)
cons x xs = Fix (Cons' x xs)
此外,Fix
允许我们在递归数据类型上定义许多泛型操作,例如折叠/展开(catamorphisms/anamorphisms)。
答案 2 :(得分:1)
data FooType = Foo FooType
allTheFoos = Foo allTheFoos
我认为有两种有用的方法来看待这种类型。
首先是"道德"方式 - 我们假装Haskell类型没有"底部" (非终止)值。从这个角度来看,FooType
是单元类型 - 只有一个值的类型,就像()
一样。这是因为如果您禁止底部,那么Foo
类型的唯一值就是您的allTheFoos
。
来自"不道德"透视(允许底部),FooType
或者是Foo
构造函数的无限塔,或者是Foo
构造函数的有限塔,其底部是底部。这类似于"道德"这种解释:
data Nat = Zero | Succ Nat
...但是底部而不是零,这意味着你不能写出像这样的函数:
plus :: Nat -> Nat -> Nat
plus Zero y = y
plus (Succ x) y = Succ (x `plus` y)
离开我们的地方?我认为结论是FooType
并不是真正有用的类型,因为:
()
。Nat
但是任何试图匹配"零"的函数。没有终止。答案 3 :(得分:1)
您的FooType的扩展名可以是抽象语法树。使用仅具有整数,求和和反转的简单示例语言,类型定义将是
data Exp = AnInt Integer
| AnInverse Exp
| ASum Exp Exp
以下所有内容都是Exp实例:
AnInt 2 -- 2
AnInverse ( AnInt 2 ) -- 1 / 2
AnInverse ( ASum ( AnInt 2 ) ( AnInt 3 ) ) -- 1 / ( 2 + 3 )
AnInverse ( ASum 1 ( AnInverse 2 ) ) -- 1 / ( 1 + 1 / 2 )
如果我们从Exp定义中删除了AnInt和ASum,那么该类型将与您的FooType同构(AnInverse替换Foo)。
答案 4 :(得分:0)
以下类型:
newtype H a b = Fn {invoke :: H b a -> b}
虽然与你的不完全相同,但具有相似的精神,但Launchbury,Krstic和Sauerwein已经展示了有关corouitining的有趣用法:https://arxiv.org/pdf/1309.5135.pdf