我正在尝试创建一个在值元组中返回自身的函数。基本上,这个想法是调用者将获得转换后的值,以及函数的新(curried)版本,以便在处理过程中进一步使用。
然而,目前,我一直试图想出这个功能的无操作(即无所事事)版本。所以下面的片段显然很好 - 这是一个不能自行返回的无操作:
noOp s as xs = (s, as, xs)
但如果我改为:
noOp s as xs = (s, as, xs, noOp)
我收到“无限类型”错误:
Occurs check: cannot construct the infinite type:
t3 = t0 -> t1 -> t2 -> (t0, t1, t2, t3)
In the expression: noop
In the expression: (s, as, xs, noop)
In an equation for `noop': noop s as xs = (s, as, xs, noop)
关于处理无限类型错误的SO有很多讨论 - 但我无法弄清楚如何应用我的问题。
欢迎任何建议......
答案 0 :(得分:9)
要表达这样的东西,你需要一个递归类型。由于Haskell不支持equirecursive types,因此您需要使用newtype
/ data
。
因此,您可以定义newtype Foo s = Foo { runFoo :: s -> (s, Foo s) }
,然后编写noOp :: Foo (A,B,C); noOp = Foo (\(a,b,c) -> ((a,b,c), noOp))
。
这看起来像Mealy machine。包machines
exports包类似:newtype Mealy i o = Mealy { runMealy :: i -> (o, Mealy i o) }
答案 1 :(得分:1)
回答@augustss试图说的话。要走的路是使用像
这样的递归类型data Foo a = Foo a (a -> Foo a)
noop :: a -> Foo a
noop a = Foo a noop
答案 2 :(得分:1)
你面临的问题是你有一个无限递归的类型!类型
的意思是noOpnoOp :: a -> b -> c -> (a,b,c,a -> b -> c -> (a,b,c,a -> b -> c -> (a,b,c,...))))
正如您所看到的,我们永远无法完全写出noOp
的类型,因为它依赖于它
关于noOp
的类型。如果我们只能封装noOp
的类型
按名称引用它。
但事实上,我们可以做到这一点!
data Foo a b c = Foo (a -> b -> c -> (a,b,c,Foo a b c))
如您所见,捕获递归是因为我们引用了类型
按Foo a b c
。现在有必要包装和解包:
runFoo (Foo f) = f
noOp s as xs = Foo (s, as, xs, noOp)
现在,我同意,这似乎有点不方便,但对于真正的应用你
可能会找到一个比Foo
更合适的数据结构来保存你的
值,可能是
data Bar s as xs = Bar s as xs (Bar s as xs)