2010-09-25 4 views
11

Ich versuche, Fehler in Haskell Fehlerbehandlung zu bekommen. Ich habe den Artikel "8 ways to report errors in Haskell" gefunden, aber ich bin verwirrt, warum Maybe und Eat sich anders verhalten.Haskell: Warum verhalten sich die Typen Maybe und Entweder anders, wenn sie als Monaden verwendet werden?

Zum Beispiel:

import Control.Monad.Error 

myDiv :: (Monad m) => Float -> Float -> m Float 
myDiv x 0 = fail "My divison by zero" 
myDiv x y = return (x/y) 

testMyDiv1 :: Float -> Float -> String 
testMyDiv1 x y = 
    case myDiv x y of 
     Left e -> e 
     Right r -> show r 

testMyDiv2 :: Float -> Float -> String 
testMyDiv2 x y = 
    case myDiv x y of 
     Nothing -> "An error" 
     Just r -> show r 

testMyDiv2 1 0 ein Ergebnis von "An error" gibt aufrufen, aber testMyDiv1 1 0 gibt Aufruf:

"*** Exception: My divison by zero 

(das Fehlen Schlusskurs Hinweis, was darauf hindeutet dies kein String aber eine Ausnahme).

Was gibt?

+1

Ich bekomme '" Meine Division durch Null "' für 'testMyDiv1 1 0' mit Mtl-1.1.0.2. Welche "Control.Monad.Error" verwendest du? – sepp2k

+0

Wie Sepp2k gesagt hat, hängt es von den 'instance Monad' Deklarationen für' Eat' und 'Maybe' ab. Idealerweise sollten Sie die Instanz-Monade (entweder String) verwenden, die in der letzten Basis verfügbar ist, oder eine von "mtl" oder "monads- {fd, tf}". –

+0

Ich merke, ich bekomme viel "Sie verwenden das alte Paket' base 'Version 3.x. Zukünftige GHC-Versionen werden nicht Basisversion 3.x unterstützen. Sie sollten Ihren Code aktualisieren, um die neue Basisversion 4.x zu verwenden. " Fehler. Also vielleicht ist meine Ghc-Installation veraltet? Kabale Aktualisierung gilt nicht. Ich denke daran, meine Installation zu löschen und neu zu starten. – stusmith

Antwort

14

Die kurze Antwort ist, dass die Monade Klasse in Haskell den fail Betrieb auf die ursprüngliche mathematische Idee von Monaden fügt hinzu, die es etwas umstritten macht, wie den Entweder-Typen in eine (Haskell) machen Monad, denn es gibt viele Möglichkeiten, TU es.

Es gibt verschiedene Implementierungen, die verschiedene Dinge ausführen. Die 3 grundlegenden Ansätze, die ich kenne, sind:

  • fail = Left. Dies scheint zu sein, was die meisten Leute erwarten, aber es kann tatsächlich nicht in strengem Haskell 98 getan werden. Die Instanz müsste als instance Monad (Either String) deklariert werden, was unter H98 nicht legal ist, weil sie einen bestimmten Typ für einen der Parameter Either erwähnt (In GHC würde die Erweiterung FlexibleInstances dazu führen, dass der Compiler sie akzeptiert).
  • Ignorieren Sie fail, verwenden Sie die Standardimplementierung, die nur error aufruft. Dies ist, was in Ihrem Beispiel passiert. Diese Version hat den Vorteil, dass sie H98-konform ist, aber den Nachteil hat, dass sie für den Benutzer ziemlich überraschend ist (mit der Überraschung, die zur Laufzeit kommt).
  • Die fail Implementierung ruft eine andere Klasse auf, um einen String in einen beliebigen Typ zu konvertieren.Dies wird in MTL Control.Monad.Error Modul, das instance Error e => Monad (Either e) erklärt. In dieser Implementierung ist fail msg = Left (strMsg msg). Dieser ist wieder legal H98, und wieder gelegentlich überraschend für Benutzer, weil es eine andere Typklasse einführt. Im Gegensatz zum letzten Beispiel kommt die Überraschung zur Kompilierzeit.
+1

Ein anderer Vorschlag zu der oben erwähnten ML-Diskussion lautete "fail msg = Left (error msg)", aber das ist aus vielen Gründen hässlich. –

4

Ich schätze, Sie verwenden monads-fd.

$ ghci t.hs -hide-package mtl 
*Main Data.List> testMyDiv1 1 0 
"*** Exception: My divison by zero 
*Main Data.List> :i Either 
... 
instance Monad (Either e) -- Defined in Control.Monad.Trans.Error 
... 

Suchen im transformers Paket, das ist, wo monads-fd die Instanz kommt, sehen wir:

instance Monad (Either e) where 
    return  = Right 
    Left l >>= _ = Left l 
    Right r >>= k = k r 

also keine Definition für Ausfallen, was so überhaupt. Im Allgemeinen wird fail abgeraten, da es nicht immer garantiert ist, dass es in einer Monade fehlerfrei funktioniert (viele Leute würden gerne sehen, dass fail aus der Monad-Klasse entfernt wird).

EDIT: Ich sollte hinzufügen, dass es sicherlich nicht klar ist fail war beabsichtigt, als Standard error Anruf verlassen werden. Ein Ping zu Haskell-Cafe oder der Betreuer könnte sich lohnen.

EDIT2: Die mtl Instanz hat moved to base gewesen, schließt dieser Schritt die Definition von fail = Left und Diskussion zu entfernen, warum die Entscheidung wurde getroffen. Vermutlich wollen sie, dass Leute ErrorT mehr benutzen, wenn Monaden versagen, und so fail für etwas mehr katastrophale Situationen wie schlechte Musterübereinstimmungen reservieren (zB: Just x <- e wo e -->* m Nothing).

+0

Die Monad-Klasse sollte auf (>> =) beschränkt sein, wenn sie von mir abhing. – kmm

+0

Was haben Sie gegen 'Rückkehr'? ;-) –

+0

@kmm Eigentlich enthält die mathematische Definition nicht "fail", aber es wurde hinzugefügt, um die ordentliche "do" -Syntax zu liefern und ist somit ein notwendiges Übel. – fuz

Verwandte Themen