Für another answer von mir aufzählt, schrieb ich den folgenden Code, bietet diagonally traversedUniverse
Instanzen für zählbare Generic
s (es ist leicht von der Version dort aktualisiert, sondern verwendet die gleiche Logik):Endlosschleife, wenn alle Werte eines generischen Instanz
{-# 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
wird wahrscheinlich nicht mit dem Problem, sondern war Teil der Frage.)
Dies ist für die meisten Arten funktioniert, auch rekursiven wie jene:
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)
Beispiele:
*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)]
Aber beachten Sie, dass oben genannten Typen haben alle ihre rekursive Konstrukteuren nicht an erster Stelle! In der Tat (und das ist das Problem), die folgende divergiert:
*Main> data T7 = T7 T7 | T7' deriving (Show, Generic)
*Main> take 5 $ (universe :: [T7])
*** Exception: <<loop>>
Ich dachte zuerst, dass vielleicht ist es etwas mit Omegas
‚s Auswerteauftrag, sondern tauscht die linke und rechte Teile in Linie (2)
macht nur T7
Arbeit, und T6
scheitern, was ich als korrektes Verhalten erwarten würde.
Mein aktueller Verdacht ist, dass der Anruf zu universe
in Zeile (1)
zu früh ausgewertet wird. Zum Beispiel weicht die folgende auch, während sollte es genau eine Wert in der Liste, die nicht einmal ausgewertet werden soll:
*Main> data T8 = T8 T8 deriving (Show, Generic)
*Main> null $ (universe :: [T8])
*** Exception: <<loop>>
Also, die einzige Instanz, T8 (T8 (...) ...)
, wird innerhalb der Liste ausgewertet , obwohl es nicht benötigt wird! Ich habe keine Ahnung, woher dieser Effekt kommt - ist es die rekursive Verwendung seiner eigenen Universe
Instanz? Aber warum verhalten sich "rekursive" Typen wie T6
korrekt, während "rekursive Links" (T7
) nicht funktionieren?
Ist das ein striktes Problem? Wenn ja, in welchem Teil des Codes? Meine Universe
Instanz? Generic
? Und wie man es repariert? Ich verwende GHC 7.6.3, wenn das wichtig ist.
Sie sind ein cooler Typ. Danke dafür (; – MaiaVictor
Gern geschehen! Ich fand das Problem selbst sehr interessant - es brachte mich dazu, Generika zu lernen. – phg
Noch warten :( – MaiaVictor