2016-05-10 4 views
1

die folgende Typdeklaration vor:kann nicht anzeigen Beispiel für diese Art schreiben

data Foobar x = Foo (x (Foobar x)) 

nun ein Show Beispiel dafür schreiben. Du traust dich ja nicht!

Ich schrieb zufällig deriving Show, erwartet, dass GHC es tut. Aber es beklagte sich, dass es das nicht konnte. "Armer Dummkopf", dachte ich und fuhr fort, es selbst zu definieren. Aber ... ähm ... oh, eigentlich ist das ziemlich interessant ... Offensichtlich ist Foobar x nur dann darstellbar, wenn x y für darstellbare y ... oder ... so etwas gezeigt werden kann ... auargh! > _ <

Jetzt bin ich sicher, dass ich nicht die erste Person sein kann, die einen Typ wie diesen definiert. Und ich kann nicht die erste Person sein, die eine Show Instanz dafür haben möchte. Also muss jemand herausgefunden haben, wie das geht. ;-)

Antwort

6

Wenn Sie nur die offensichtliche Einschränkung angeben - und einige notwendige Sprachpragmas hinzufügen -, funktioniert alles einwandfrei. Damit Foobar zur Anzeige gebracht werden kann, muss das Feld Foo darstellbar sein, so dass wir eine Show (x (Foobar x)) Einschränkung benötigen. Und wenn wir danach fragen, alles funktioniert nur aus:

{-# LANGUAGE FlexibleContexts, UndecidableInstances, StandaloneDeriving #-} 

data Foobar x = Foo (x (Foobar x)) 
deriving instance Show (x (Foobar x)) => Show (Foobar x) 

Dann:

λ> print $ Foo [Foo [], Foo [Foo []]] 
Foo [Foo [],Foo [Foo []]] 
λ> data SL a = S String | L [a] deriving Show 
λ> print $ Foo (L [Foo (S "a"), Foo (L [Foo (S "b"), Foo (L []), Foo (S "c")])]) 
Foo (L [Foo (S "a"),Foo (L [Foo (S "b"),Foo (L []),Foo (S "c")])]) 

Ich bin ein wenig überrascht, dies funktioniert, aber nicht übermäßig :-)


das ist übrigens genau das, was GHC von euch fragt, ob Sie versuchen, Show für Foobar abzuleiten:

λ> data Foobar x = Foo (x (Foobar x)) deriving Show 

<interactive>:2:45: 
    No instance for (Show (x (Foobar x))) 
     arising from the first field of ‘Foo’ (type ‘x (Foobar x)’) 
    Possible fix: 
     use a standalone 'deriving instance' declaration, 
     so you can specify the instance context yourself 
    When deriving the instance for (Show (Foobar x)) 

Alles, was wir tun mussten, war StandaloneDeriving und genau diese Einschränkung angeben!


, auch klar zu sein, der StandaloneDeriving Teil ist optional - es jede Magie nicht tut, und die Instanz ist nicht allzu schwer von Hand zu schreiben:

instance Show (x (Foobar x)) => Show (Foobar x) where 
    showsPrec p (Foo x) = showParen (p > app_prec) $ 
          showString "Foo " . showsPrec (app_prec + 1) x 
    where app_prec = 10 :: Int 
+2

Das ist [wie 'Show' definiert ist für' Fix'] (https://hackage.haskell.org/package/recursion-schemes-4.1.2/docs/src/Data-Functor -Foldable.html # Fix) in Rekursionschemata. – Cirdec

+1

Ah ja, das macht Sinn - denn das * ist * 'Fix', bis zum Umbenennen! Das hätte ich sofort bemerken müssen :-) –

+0

Ich war mir sicher, dass dieser Typ schon irgendwo in den Bibliotheken war, aber ich konnte ihn aus irgendeinem Grund nicht sehen ... – MathematicalOrchid

1

OK, also, während mit dem Code zu spielen, fand ich eine Art und Weise tun:

instance (Functor f, Show (f String)) => Show (Foobar f) where 
    show = fold show 

fold :: Functor f => (f x -> x) -> Foobar f -> x 
fold f (Foo x) = f (fmap (fold f) x) 

Es gibt ein paar obwohl von Problemen.

  • Es produziert nicht genau die richtige Ausgabe. (Der Foo-Konstruktor fehlt, und Teilausdrücke werden in Anführungszeichen gesetzt.)
  • Es erfordert offenbar UndecidableInstances zu arbeiten. (!)
  • Ich versuchte show zu ändern, um den Foo Konstruktor enthalten, aber dann GHC in Panik geraten (!!!)

Ich wusste nicht, Sie könnten tatsächlich Absturz GHC nur durch Benutzercode zu schreiben. .. anscheinend kannst du das tun.

ghc: panic! (the 'impossible' happened) 
    (GHC version 7.4.2 for i386-unknown-mingw32): 
    kindFunResult ghc-prim:GHC.Prim.*{(w) tc 34d} 

Please report this as a GHC bug: http://www.haskell.org/ghc/reportabug 

Ich denke, es „unentscheidbar“ für einen Grund genannt! o_O

+2

Diese Panik hat nichts mit (das korrekte Verhalten von) 'UndecidableInstances' zu tun - das Schlimmste, was'UndecidableInstances' tun kann (oder zumindest tun können sollte!) Ist die Art Checker-Schleife. Ich würde sagen, das als Bug zu melden, aber Sie befinden sich anscheinend auf einer ziemlich alten GHC-Version. Wenn Sie diesen Code so bearbeiten, dass der Code in Panik gerät, kann ich ihn auf einem neueren GHC testen. –

+0

OK, danke. Gute Neuigkeiten: Ich konnte keine Panik mit Ihrem Code unter GHC 7.10.2 unter Mac OS X 10.10.5 reproduzieren. –

+0

Ändern Sie "fold show" in "fold" (\ x -> "Foo (" ++ show x ++ ")") ''. Es gerät in Panik 7.4.2. Ich habe es mit 7.6.x versucht und es hat nicht in Panik geraten, also bin ich mir ziemlich sicher, was auch immer dieser Fehler ist, es ist längst behoben. – MathematicalOrchid

6

Hier ist, wie zu tun es ohne Erweiterungen:

data Foobar x = Foo (x (Foobar x)) 

class Show1 f where 
    showsPrec1 :: Show a => Int -> f a -> ShowS 

instance Show1 f => Show (Foobar f) where 
    showsPrec n (Foo v) = ("Foo " ++) . showsPrec1 n v -- doesn't handle n correctly because that's not the point of the question and would only distract from the actual important idea 

Die Show1 Klasse (und einige Fälle für Standardtypen) ist auch on Hackage zur Verfügung. Das Modul, das es definiert, verwendet ein paar Erweiterungen - aber nur, um den Standard zu reduzieren und nichts zu ermöglichen, was langweilige alte Haskell2010 nicht kann.