【问题标题】:Infinite recursion when enumerating all values of a Generic instance枚举通用实例的所有值时无限递归
【发布时间】:2014-06-26 04:07:54
【问题描述】:

对于我的another answer,我编写了以下代码,为可枚举的Generics 提供diagonally traversedUniverse 实例(它从那里的版本略有更新,但使用相同的逻辑):

{-# LANGUAGE DeriveGeneric, TypeOperators, ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances, FlexibleContexts, DefaultSignatures #-}
{-# LANGUAGE UndecidableInstances, OverlappingInstances #-}

import Data.Universe
import Control.Monad.Omega
import GHC.Generics
import Control.Monad (mplus, liftM2)

class GUniverse f where
    guniverse :: Omega (f x)

instance GUniverse U1 where
    guniverse = return U1

instance (Universe c) => GUniverse (K1 i c) where
    guniverse = fmap K1 $ each (universe :: [c])        -- (1)

instance (GUniverse f) => GUniverse (M1 i c f) where
    guniverse = fmap M1 (guniverse :: Omega (f p))

instance (GUniverse f, GUniverse g) => GUniverse (f :*: g) where
    guniverse = liftM2 (:*:) ls rs
        where ls = (guniverse :: Omega (f p))
              rs = (guniverse :: Omega (g p))

instance (GUniverse f, GUniverse g) => GUniverse (f :+: g) where
    guniverse = (fmap L1 $ ls) `mplus` (fmap R1 $ rs)   -- (2)
        where ls = (guniverse :: Omega (f p))
              rs = (guniverse :: Omega (g p))

instance (Generic a, GUniverse (Rep a)) => Universe a where
    universe = runOmega $ fmap to $ (guniverse :: Omega (Rep a x))

Omega 可能与问题无关,但是问题的一部分。)

这适用于大多数类型,甚至是递归类型:

data T6 = T6' | T6 T6 deriving (Show, Generic)
data T = A | B T | C T T deriving (Show, Generic)
data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show, Generic, Eq)

例子:

*Main> take 5 $ (universe :: [T6])
[T6',T6 T6',T6 (T6 T6'),T6 (T6 (T6 T6')),T6 (T6 (T6 (T6 T6')))]
*Main> take 5 $ (universe :: [T])
[A,B A,B (B A),C A A,B (B (B A))]
*Main> take 5 $ (universe :: [Tree Bool])
[Leaf False,Leaf True,Branch (Leaf False) (Leaf False),Branch (Leaf False) (Leaf True),Branch (Leaf True) (Leaf False)]

但请注意,上述类型都有其递归构造函数一开始就没有!事实上(这就是问题所在),存在以下分歧:

*Main> data T7 = T7 T7 | T7' deriving (Show, Generic)
*Main> take 5 $ (universe :: [T7])
*** Exception: <<loop>>

我首先认为Omegas 的评估顺序可能有些问题,但是在(2) 行中交换左右部分只会使T7 工作,而T6 失败,这就是我想要的期望正确的行为。

我目前的怀疑是对(1) 行中的universe 的调用评估得太早了。例如,以下内容也存在分歧,而列表中应该恰好有 一个 值,甚至不应该对其进行评估:

*Main> data T8 = T8 T8  deriving (Show, Generic)
*Main> null $ (universe :: [T8])
*** Exception: <<loop>>

所以,唯一的实例 T8 (T8 (...) ... ) 被评估在列表中,即使它不是必需的!我不知道这个效果是从哪里来的——它是递归使用它自己的Universe 实例吗?但是,为什么像 T6 这样的“右递归”类型表现正确,而“左递归”类型 (T7) 却没有?

这是一个严格的问题吗?如果是这样,在代码的哪一部分?我的Universe 实例? Generic?以及如何解决?如果这很重要,我使用 GHC 7.6.3。

【问题讨论】:

  • 你是个很酷的人。谢谢你(;
  • 不客气!我自己发现这个问题很有趣——它让我学习了泛型。

标签: haskell strictness ghc-generics


【解决方案1】:

T8 这样的类型无法生成。让我们看看 T8 的 Universe 的泛型版本实际上简化为:

t8Universe :: [T8]
t8Universe = fmap T8 t8Universe

在任何时候都不会产生 (:) 或 []。如果没有另一个非递归构造函数来成功生成,就无法取得进展。 t8Universe 的元素数量与 t8Universe 的元素数量完全相同,但这是循环的,因此是求值循环。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多