终于有时间写一个generic的版本了。它使用Universe 类型类,它表示递归可枚举类型。这里是:
{-# LANGUAGE DeriveGeneric, TypeOperators, ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances, OverlappingInstances #-}
import Data.Universe
import Control.Monad.Omega
import GHC.Generics
import Control.Monad (mplus, liftM2)
class GUniverse f where
guniverse :: [f a]
instance GUniverse U1 where
guniverse = [U1]
instance (Universe c) => GUniverse (K1 i c) where
guniverse = fmap K1 (universe :: [c])
instance (GUniverse f) => GUniverse (M1 i c f) where
guniverse = fmap M1 (guniverse :: [f p])
instance (GUniverse f, GUniverse g) => GUniverse (f :*: g) where
guniverse = runOmega $ liftM2 (:*:) ls rs
where ls = each (guniverse :: [f p])
rs = each (guniverse :: [g p])
instance (GUniverse f, GUniverse g) => GUniverse (f :+: g) where
guniverse = runOmega $ (fmap L1 $ ls) `mplus` (fmap R1 $ rs)
where ls = each (guniverse :: [f p])
rs = each (guniverse :: [g p])
instance (Generic a, GUniverse (Rep a)) => Universe a where
universe = fmap to $ (guniverse :: [Rep a x])
data T = A | B T | C T T deriving (Show, Generic)
data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show, Generic)
我找不到删除UndecidableInstances 的方法,但这应该没什么大不了的。 OverlappingInstances 只需要覆盖预定义的Universe 实例,例如Either。现在有一些不错的输出:
*Main> take 10 $ (universe :: [T])
[A,B A,B (B A),C A A,B (B (B A)),C A (B A),B (C A A),C (B A) A,B (B (B (B A))),C A (B (B A))]
*Main> take 20 $ (universe :: [Either Int Char])
[Left (-9223372036854775808),Right '\NUL',Left (-9223372036854775807),Right '\SOH',Left (-9223372036854775806),Right '\STX',Left (-9223372036854775805),Right '\ETX',Left (-9223372036854775804),Right '\EOT',Left (-9223372036854775803),Right '\ENQ',Left (-9223372036854775802),Right '\ACK',Left (-9223372036854775801),Right '\a',Left (-9223372036854775800),Right '\b',Left (-9223372036854775799),Right '\t']
*Main> take 10 $ (universe :: [Tree Bool])
[Leaf False,Leaf True,Branch (Leaf False) (Leaf False),Branch (Leaf False) (Leaf True),Branch (Leaf True) (Leaf False),Branch (Leaf False) (Branch (Leaf False) (Leaf False)),Branch (Leaf True) (Leaf True),Branch (Branch (Leaf False) (Leaf False)) (Leaf False),Branch (Leaf False) (Branch (Leaf False) (Leaf True)),Branch (Leaf True) (Branch (Leaf False) (Leaf False))]
我不确定mplus 的分支顺序会发生什么,但我认为如果Omega 正确实施,一切都会解决,我坚信这一点。
但是等等!上面的实现还不是没有错误的;它在“左递归”类型上有所不同,如下所示:
data T3 = T3 T3 | T3' deriving (Show, Generic)
虽然这有效:
data T6 = T6' | T6 T6 deriving (Show, Generic)
我看看能不能解决这个问题。 编辑:在某个时候,这个问题的解决方案可能会在 in this question 找到。