具有单个构造函数的自引用数据类型不能为“Show”n

Self-referential data type with single constructor cannot be `Show`n

这是来自The Little MLer。我有 this

data Chain = Link Int (Int -> Chain)

还有这个

ints :: Int -> Chain
ints n = Link (n+1) ints

我对这里到底发生了什么感到很困惑。这似乎是一个无休止的递归本身,其中左侧的 ints 只是无休止地重复整个 ints。书上说

[...] we may think of ints as a very long sequence of ints. We go from one element in this sequence to the next by applying the second component of ints n to the first.

我不确定它是如何做到的。但是这会产生一个错误

> ints 0
* No instance for (Show Chain) arising from a use of `print'
:     * In a stmt of an interactive GHCi command: print it

deriving Show 上的尝试不会成功

data Chain = Link Int (Int -> Chain) deriving Show
No instance for (Show (Int -> Chain))
        arising from the second field of `Link' (type `Int -> Chain')
        (maybe you haven't applied a function to enough arguments?)
      Possible fix:
        use a standalone 'deriving instance' declaration,
          so you can specify the instance context yourself
    * When deriving the instance for (Show Chain)

不确定发生了什么或如何进行。任何类似的例子将不胜感激。


更新

这是 SML 代码

datatype chain = Link of (int * (int -> chain))
fun ints (n) = Link (n+1, ints)
> ints(0)
val it = Link (1,fn) : chain

不完全确定,但 fn 是 SML 匿名方式,即 fn 是他们的 \。这可能只是巧合。

那么 SML 有什么 Haskell 无法处理的?这与 Haskell 是纯的而 SML 不是吗?

一般来说Show函数没有好的方法,所以Haskell不会在涉及到函数时为你创建Show的实例..

你可以自己写一个:

instance Show Chain where
  show (Link n fn) = ...

但是现在你必须弄清楚如何显示fn:: Int->Chain。在 Haskell 中至少函数是原子的和不透明的。您不能拆开它们或检查它们的内容,只能应用它们。

这是怎么回事:

current :: Chain -> Int
current (Link i _) = i

next :: Chain -> Chain
next (Link i g) = g i

正如引述所说。你的 ints 是一个函数,所以这里没有无限的事情发生,只是函数应用程序延迟的可能性来获得链中的 next Link 所以下一个Int 可以找到 current 元素。

至于打印,

printLink :: Link -> String
printLink (Link i _) = "{Link of " ++ show i ++ " }"

是一种可能的定义。关于该函数,我们没有什么可以提供的信息,所以我们只能忽略它。

互动示例:

ch1 = ints 0
i1 = current ch1  -- 1
ch2 = next ch1  
i2 = current ch2  -- 2

等等

接下来我们可以定义takeN :: Int -> Chain -> [Int] 使得takeN 5 ch1 returns 来自链ch1 的五个Int 的列表,即[1,2,3,4,5].

SML 是严格的,它不能像 Haskell 那样有无限的列表。因此,SML 表示函数的“下一个”计算, 必须显式调用该函数。

由于 Haskell 是惰性的,我们也只是定义 chainToList :: Chain -> [Int] 将链转换为其中的无限条目列表,然后在其上使用标准函数 take .

在您的新 SML 示例中,“fn”只是一个指示器,表明那里有 a 函数,some 函数.在你的情况下,它是函数 ints 但 运行-time 系统显然不知道。这可能意味着 any 函数将在 SML 中打印为“fn”,如您的示例所示。

但您无需打印即可使用。

这是一个没有在文本中阅读足够远的情况,以及从一种语言 (SML) 而不是另一种语言得到误报的情况。事实证明,让 Chain 返回函数像 ints 来做直接输出是一个误用。这些 Chain “生成器”只是作为另一个函数的中介,用于“查找”这样一个 Chain 对象的位置,在 TLMLer 的示例中,数据第二部分中的下一个整数构造函数。

data Chain = Link Int (Int -> Chain)

所以考虑另一个 Chain 生成器 fiveOrsevenInts

mod5or7 n = if (evenDiv n 5) then True else (evenDiv n 7)
                where evenDiv a b = ((a `mod` b) == 0)

fiveOrsevenInts n = if (mod5or7 (n+1))
             then (Link (n+1) fiveOrsevenInts)
             else fiveOrsevenInts (n+1)

fiveOrsevenInts 根据可被 5 或 7 整除的数字创建 Chain。据我了解,整个想法是“提前阅读”“链”。所以如果

fiveOrsevenInts (1) => Link 5 fiveOrsevenInts

诀窍是让 fiveOrsevenIntsInt -> Chain 函数)作用于 5 以获得链中的下一个 Int。而正如我错误地认为的那样,我们根本不需要“看到”上述直接解析fiveOrsevenInts (1)

在链序列中占据所需位置的通用函数和像 fiveOrsevenInts 这样的链生成函数并返回该位置的成员将是

chainItem n (Link i f) = if (n == 1)
                         then i
                         else chainItem (n-1) (f i)

现在,如果我想查看 5 或 7 的整数倍数序列中的第一个,只需

chainItem 1 (fiveOrsevenInts 0)
5
chainItem 2  (fiveOrsevenInts 0)
7
chainItem 3  (fiveOrsevenInts 0)
10
...
chainItem 37  (fiveOrsevenInts 0)
119

其中 fiveOrsevenInts 0“种子”Chain。在本章的后面,该策略将用于通过构建素数 Chain 生成器来查找素数序列。总而言之,我发现 The Little MLer 是一种学习其兄弟 Haskell 的有趣方式。当我完成后,我会尝试分享我的整体翻译。