【问题标题】:Infinite type error when returning a self-referential function in haskell在haskell中返回自引用函数时出现无限类型错误
【发布时间】:2013-08-15 22:41:43
【问题描述】:

我正在尝试创建一个以值元组形式返回自身的函数。基本上,这个想法是调用者将返回转换后的值,以及一个新的(curried)版本的函数,以便在处理中进一步使用。

但是,目前,我一直在尝试提出此功能的无操作(即无操作)版本。所以下面的 sn-p 显然没问题 - 这是一个不返回自身的空操作:

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 的讨论 - 但我不太清楚如何应用到我的问题。

欢迎任何建议...

【问题讨论】:

  • 为什么要再次返回相同的函数?
  • 尝试写类型,你就会明白为什么 ghc 抱怨了。
  • 在某处引入一个新类型(datanewtype)以创建递归类型。例如,为noOp的返回类型创建一个新类型。
  • 我再次返回相同的函数,因为正常使用将是一个柯里化函数,它接受一个带有一些状态信息的参数。所以基本思想是真正的(不是无操作版本)函数将返回具有不同状态的自身的新版本。

标签: haskell infinite


【解决方案1】:

要表达这样的内容,您需要一个递归类型。由于 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。包machinesexports类似类型:newtype Mealy i o = Mealy { runMealy :: i -> (o, Mealy i o) }

【讨论】:

  • 我不确定我是否完全理解如何使用它。我将如何创建一个新的 Foo,调用 noOp 函数,然后从回报中提取 a、b、c 和 noOp 基金的元组?为这个愚蠢的问题道歉 - 我是 Haskell 的新手,在为自己尝试实验的同时完成教程......
  • 我在那里举了一个例子。如果您将 (A,B,C) 更改为您实际拥有的某些类型,它将编译。例如。对于Int 状态,你可以写成noOp :: Foo Int; noOp = Foo (\x -> (x, noOp))。然后noOp :: Foo Int。要从中获取功能,您可以使用 runFoo: runFoo noOp :: Int -> (Int, Foo Int)
【解决方案2】:

只是回答@augustss 想说的话。要走的路是使用递归类型,如

data Foo a = Foo a (a -> Foo a)
noop :: a -> Foo a
noop a = Foo a noop

【讨论】:

  • 对于它的价值,与我的回答有关,这个FooMoore machinedata Moore i o = Moore o (i -> Moore i o)。但是您也可以根据彼此来定义它们:Mealy i o = i -> Moore i o; Moore i o = (o, Mealy i o).
【解决方案3】:

你面临的问题是你有一个无限递归的类型!类型 noOp 类似于

noOp :: 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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 2016-11-01
    • 1970-01-01
    相关资源
    最近更新 更多