Hallo.
This seems very related to the bug I reported ago.
ghc -fno-prune-tydecls -O2 -fallow-undecidable-instances \
-fglasgow-exts -fallow-overlapping-instances -c -o Arglib.o Arglib.hs
panic! (the `impossible' happened):
mkWWcpr: not a product
forall tzq{-rme-}.
Arglib.ArgrF{-r1H,x-}
g{-rll-}
Arglib.GenArgError{-r2T,x-}
(PrelMaybe.Maybe{-r8U,i-} t{-rlm-} -> tzq{-rme-})
tzq{-rme-}
bye
{- Anwendungen:
- CGI-Abfrageparameter
- HTML-Element-Attribute
- Datenbankabfrageergebnisse
- Kommandozeilenparameter
-}
module Arglib (
-- Grundtypen
Query, Row,
-- Argumentleser-Schemata
Argr(..), ArgrF, ArgrC, alf, alc,
argErrorIo, alfIo, alcIo,
-- Argumentleser
Arg(..), Argn(..), Argd(..), Argnd(..),
args, argns, argm, argnm,
arg_gen, argn_gen, argd_gen, argnd_gen,
-- Argumentschreiber
(\.), Argb(..), Argbn(..),
argb2, argbn1, argbn2, argbn3, argbn4, blq, blt,
liftkomp, komp1, komp2, komp3, komp4, komp5, komp6, komp7, komp8, komp9,
-- Bausteine f�r generische Argumentleser
GenParser(..), GenReader(..), GenWriter(..), GenNameExtractor(..),
GenExtractor(..), GenArgError(..)
) where
import Char
import Numeric (readSigned, readDec)
{--------------------------------------------------------------------------
Grundwerte
--------------------------------------------------------------------------}
-- Name/Wert-Liste, z.B. aus einer CGI-Abfrage
type Query = [(String,String)]
data QueryArgError = QueryArgError_Missing String
queryGenNameExtractor :: (String -> e) -> String -> Query -> (Either String e, Query)
queryGenNameExtractor mkerr name q =
(case (lookupi name q) of
Just val -> Left val
Nothing -> Right (mkerr name),
q)
where lookupi _ [] = Nothing
lookupi w ((x,y):li) = if (map toLower x) == (map toLower w)
then Just y
else lookupi w li
instance GenNameExtractor Query GenArgError where
genNameExtractor = queryGenNameExtractor (\name -> ArgError name "fehlt")
instance GenNameExtractor Query QueryArgError where
genNameExtractor = queryGenNameExtractor QueryArgError_Missing
{-
instance ToGenArgError QueryArgError where
toGenArgError qae = case qae of
QueryArgError_Missing str -> ArgError str "fehlt"
-}
-- Liste von vielleicht vorhandenen Strings, z.B. ein
-- SQL-Datenbank-Abfrageergebnis mit eventuellen NULL-Werten
type Row = [Maybe String]
data RowArgError = RowArgError_Missing Int
| RowArgError_Null Int
| RowArgError_Parse Int String
{-
instance ToGenArgError RowArgError where
toGenArgError rae = case rae of
RowArgError_Missing nr -> ArgListError "zu wenige Argumente"
RowArgError_Null nr -> ArgError (show nr) "unzul�ssiger Nullwert"
RowArgError_Parse nr txt -> ArgError (show nr)
("Parsfehler bei \"" ++ txt "\"")
-}
instance GenExtractor Row RowArgError where
genExtractor [] = (Right (RowArgError_Missing 0{---}), [])
genExtractor (Nothing:xs) = (Right (RowArgError_Null 0{---}), xs)
genExtractor (Just str:xs) = (Left str, xs)
instance GenExtractor Row GenArgError where
genExtractor [] = (Right (ArgListError "zu wenige Attribute"), [])
genExtractor (Nothing:xs) = (Right (ArgError "{---}" "fehlt"), xs)
genExtractor (Just str:xs) = (Left str, xs)
instance GenNameExtractor Row RowArgError where
genNameExtractor _ = genExtractor
instance GenNameExtractor Row GenArgError where
genNameExtractor _ = genExtractor
{--------------------------------------------------------------------------
Argumentleser-Schemata
---------------------------------------------------------------------------
Konstruktorklasse f�r Argumentlistenschemata mit folgenden Parametern:
1. der Typ der zu transformierenden Funktion
2. der Grundtyp
3. der Typ des Ergebnisses der fertig transformierten Funktion, falls
alle Argumente gelesen werden konnten
In einem Schema des Typs "argr a g b" enth�lt eine Transformierungs-
funktion, die aus einer Funktion der Signatur "a1 -> a2 -> ... -> b"
eine der Signatur "g -> b" macht. Dabei ist a = "a1 -> a2 -> ... -> b".
b ist also ein "Suffix" von a. Das kann jedoch nicht direkt ausgedr�ckt
werden, weil etwas der Form "x -> y" zu dem sehr verschiedenen Typ "(x1
-> x2 -> ...) -> y" f�hren w�rde.
Die als a und b auftretenden Typen sind f�r jede konstruierte Argument-
liste - und innerhalb jeder solchen - anders. Ein Argumentleserschema
ist ein Typkonstruktor, und darauf ausgelegt, zusammen mit polymorphen
Argumentleser-Konstruktoren und -Kombinatoren verwendet zu werden.
-}
class Argr argr2 where
{- Kombinator f�r Argumentleser. Dies baut aus zwei Argumentlesern einen
neuen, der die vereinten Argumente liest. Z.B. aus zwei
Argumentlesern f�r je ein Argument einen f�r zwei Argumente. -}
infixl 8 /.
(/.) :: argr2 a b
-> argr2 b c
-> argr2 a c
{- Eine Art Komposition eines Schemas mit einer normalen Funktion. Ein
oder mehrere Argumente werden, nachdem und falls sie korrekte geparst
werden konnten, nicht direkt an die zu transformierende Funktion
�bergeben, sondern vorher durch eine andere Funktion geleitet. Diese
macht ein einziges Argument daraus, das dann �bergeben wird.
Ein Schema f�r mehrere Argumente transformiert eine mehrargumentigen
Funktion eine, die diese Argumente aus dem Grundwert liest. Im Falle
eines solchen wird es durch ($.) zu einem, das dieselben Argumente
liest, diese jedoch der mehrargumentigen dazukomponierten Funktion
�bergibt. Deren eines Ergebnis wird als Argument an die Funktion
weitergegeben, auf die das Schema angewendet wird. Die Komposition
macht also aus einem Schema, das mehrargumentige Funktionen
transforiert, eins das einargumentige transformiert.
-}
infix 9 $.
($.) :: f -- dazwischenzuh�ngende Funktion
-> argr2 f v -- altes Schema
-> argr2 (v -> v') v' -- neues, einargumentiges Schema
---------------------------------------------------------------------------
-- Schema ArgrF
-- Argumentschema zum Liefern nur des ersten Fehlers. Das Funktionsergebnis
-- der transformierten Funktion besteht in entweder dem weitergereichten
-- Grundwert und dem Ergebnis der transformierten Funktion oder einem
-- einzelnen Fehler.
data ArgrF g -- Grundtyp
e -- Fehlertyp
f -- Typ der zu transformierenden Funktionen
v -- Ergebnistyp der fertig transformierten Funktion
= ArgrF (f -> g -> Either (g,v) e)
instance Argr (ArgrF g e) where
(/.) = argrf
($.) = komp_ArgrF
instance GenReader ArgrF where
genReader = genReader_ArgrF
-- ArgrF verwendet die generischen Argumentleser
-- Instanzdeklarationen 1 und 3 bzw 2 und 4 �berlappen sich. 3 bzw 4 sind
-- jedoch spezieller als 1 bzw 2, also geht es mit
-- -fallow-overlapping-instances.
instance (GenParser t, GenExtractor g GenArgError)
=> Arg (ArgrF g GenArgError) t where
arg = arg_gen
instance (GenParser t, GenNameExtractor g GenArgError)
=> Argn (ArgrF g GenArgError) t where
argn = argn_gen
instance (GenParser t, GenExtractor g GenArgError)
=> Arg (ArgrF g GenArgError) (Maybe t) where
arg = argm
instance (GenParser t, GenNameExtractor g GenArgError)
=> Argn (ArgrF g GenArgError) (Maybe t) where
argn = argnm
instance (GenParser t, GenExtractor g GenArgError)
=> Argd (ArgrF g GenArgError) t where
argd = argd_gen
instance (GenParser t, GenNameExtractor g GenArgError)
=> Argnd (ArgrF g GenArgError) t where
argnd = argnd_gen
-- Generischer Argumentleser f�r Schema ArgrF; parametrisch
-- �berladen im Typ des gelesenen Arguments und dem Grundtyp
genReader_ArgrF :: (g -> (Either String e, g))
-> Maybe t
-> (String -> Maybe t)
-> Either t (String -> e)
-> ArgrF g e (t -> t') t'
genReader_ArgrF extr extrfb parse parsefb =
ArgrF re where
re fu g =
bindReader extr extrfb parse parsefb g
(\g' val -> Left (g', fu val))
(\_ err -> Right err)
-- Argumentleser-Kombinator f�r ArgrF
argrf :: ArgrF g e a b
-> ArgrF g e b c
-> ArgrF g e a c
argrf (ArgrF r1) (ArgrF r2)
= ArgrF (\fu g -> case r1 fu g
of Left (g',fu') -> r2 fu' g'
Right err -> Right err)
-- Argumentleser-Komponierer f�r ArgrF
komp_ArgrF :: f
-> ArgrF g e f v
-> ArgrF g e (v -> v') v'
komp_ArgrF f (ArgrF re) =
ArgrF (\fu g -> case re f g
of Left (g', erg) -> Left (g', fu erg)
Right err -> Right err)
---------------------------------------------------------------------------
-- Schema ArgrC
-- Argumentschema zum Liefern aller Fehler. Alle Argumente werden geparst.
-- Jeder Einzelleser erzeugt eine Funktion der Art ($ x), die also eine zu
-- transformierende Funktion auf ein festes Argument anwendet; sofern
-- erfolgreich geparst werden konnte. Andernfalls gibt es eine Fehlerliste.
-- Ein mit einer arg...-Funktion erzeugter Argumentleser wird i.A. eine nur
-- einelementige Liste liefern. Der Kombinator argc vereint jedoch mehrere
-- Listen. Zus�tzlich wird der m�glicherweise ver�nderte Grundwert
-- weitergegeben.
data ArgrC g -- Grundtyp
e -- Fehlertyp
f -- Typ der zu verarbeitenden Funktion
v -- Ergebnistyp der transformierten Funktion, also f mit
-- weniger Argumenten
= ArgrC (g -> (g, Either (f -> v) [e]))
instance Argr (ArgrC g e) where
(/.) = argrc
($.) = komp_ArgrC
instance GenReader ArgrC where
genReader = genReader_ArgrC
-- ArgrC verwendet die generischen Argumentleser
-- Instanzdeklarationen 1 und 3 bzw 2 und 4 �berlappen sich. 3 bzw 4 sind
-- jedoch spezieller als 1 bzw 2, also geht es mit
-- -fallow-overlapping-instances.
instance (GenParser t, GenExtractor g GenArgError)
=> Arg (ArgrC g GenArgError) t where
arg = arg_gen
instance (GenParser t, GenNameExtractor g GenArgError)
=> Argn (ArgrC g GenArgError) t where
argn = argn_gen
instance (GenParser t, GenExtractor g GenArgError)
=> Arg (ArgrC g GenArgError) (Maybe t) where
arg = argm
instance (GenParser t, GenNameExtractor g GenArgError)
=> Argn (ArgrC g GenArgError) (Maybe t) where
argn = argnm
instance (GenParser t, GenExtractor g GenArgError)
=> Argd (ArgrC g GenArgError) t where
argd = argd_gen
instance (GenParser t, GenNameExtractor g GenArgError)
=> Argnd (ArgrC g GenArgError) t where
argnd = argnd_gen
-- Argumentleser-Kombinator f�r ArgrC
argrc :: ArgrC g e a b -> ArgrC g e b c -> ArgrC g e a c
argrc (ArgrC r1) (ArgrC r2) =
ArgrC (\g ->
let (g', v') = r1 g
(g'', v'') = r2 g'
in (g'', case (v', v'') of
(Left tr', Left tr'') -> Left (tr'' . tr')
(Left _, Right errl) -> Right errl
(Right errl, Left _) -> Right errl
(Right errl1, Right errl2) -> Right (errl1 ++ errl2) ))
-- Generischer Argumentleser f�r Schema ArgrC; parametrisch
-- �berladen im Typ des gelesenen Arguments und dem Grundtyp
genReader_ArgrC :: (g -> (Either String e, g))
-> Maybe t
-> (String -> Maybe t)
-> Either t (String -> e)
-> ArgrC g e (t -> t') t'
genReader_ArgrC extr extrfb parse parsefb =
ArgrC (\g ->
bindReader extr extrfb parse parsefb g
(\g' val -> (g', Left (\fu -> fu val)))
(\g' err -> (g', Right [err])))
-- Argumentleser-Komponierer f�r ArgrC
komp_ArgrC :: f -> ArgrC g e f v -> ArgrC g e (v -> v') v'
komp_ArgrC f (ArgrC re) =
ArgrC (\g -> let (g', val) = re g
in (g', case val
of Left tr -> Left (\f' -> f' (tr f))
Right errl -> Right errl))
{--------------------------------------------------------------------------
Generische Argumentleser
---------------------------------------------------------------------------
Diese Argumentleser sind parametrisch �berladen
1. mittels Parsern f�r Argumenttypen
2. mittels Extrahierern f�r Grundtypen
3. mittels generischen Argumentlesern f�r Argumentschemata
Auf den rechten Seiten kommen nur Typvariablen vor. Das ist
normalerweise nicht erlaubt, kann aber mit -fallow-undecidable-instances
zul�ssiggemacht werden. Siehe
"http://localhost/usr.doc/ghc-4.06/docs/users_guide/users_guide/\
\multi-param-type-classes.html#AEN4641"
Der Compiler schlie�t von argr �ber GenReader auf e. (?)
-}
arg_gen :: (GenParser t, GenExtractor g GenArgError,
GenReader argr4)
=> argr4 g GenArgError (t -> t') t'
arg_gen =
genReader genExtractor Nothing genParser (Right (ArgFormatError "{---}"))
argn_gen :: (GenParser t, GenNameExtractor g GenArgError,
GenReader argr4)
=> String
-> argr4 g GenArgError (t -> t') t'
argn_gen name =
genReader (genNameExtractor name) Nothing genParser (Right (ArgFormatError
"{---}"))
-- Die Leser mit Normalwert verwenden diesen bei Extrahier-Fehlschlag
-- (Grund egal). Bei Parsfehler gibt es weiterhin eine Fehlermeldung.
argd_gen :: (GenParser t, GenExtractor g GenArgError, GenReader argr4)
=> t -> argr4 g GenArgError (t -> t') t'
argd_gen def =
genReader genExtractor (Just def) genParser (Right (ArgFormatError "{---}"))
argnd_gen :: (GenParser t, GenReader argr4, GenNameExtractor g GenArgError)
=> String -> t -> argr4 g GenArgError (t -> t') t'
argnd_gen name def =
genReader (genNameExtractor name) (Just def) genParser (Right (ArgFormatError
"{---}"))
{--------------------------------------------------------------------------
Abschlie�er
---------------------------------------------------------------------------
Der Abschlie�er wendet ein Argumentschema auf eine zu transformierende
Funktion an, und liefert die transformierte Funktion. Es stehen
verschiedene Abschlie�er zur Verf�gung. Der Abschlie�er bestimmt das
verwendete Leserschema. Die transformiete Funktion ist jedoch immernoch
polymorph im Grundtyp und Fehlertyp.
-}
{- Abschlie�er f�r ArgrF. Es wird nur der ersten Fehler geliefert, und
eventuelle �berz�hlige Argumente werden ignoriert. -}
alf :: ArgrF g e f v
-> f
-> (g -> (Either v e))
alf (ArgrF transff) fu g =
case (transff fu) g of
Left (_,erg) -> Left erg
Right err -> Right err
{- Abschlie�er f�r ArgrC. Es werden alle Argumente geparst, und alle
auftretenden Fehler geliefert. �berz�hlige Argumente werden ignoriert. -}
alc :: ArgrC g e f v
-> f
-> (g -> (Either v [e]))
alc (ArgrC re) fu g =
case re g of
(_, Left transf) -> Left (transf fu)
(_, Right errl) -> Right errl
{- Hilfsfunktion f�r den Fall, da� die transformierte Funktion die Form
"t -> t' -> ... -> IO u" hat. Die Anwendung eines Argumentlesers erg�be
etwas Unpraktisches der Form "t -> t' -> ... -> Either (IO u) e", mit
einem Fehlertyp e. Praktischer ist die Form "t -> t' -> ... ->
IO (Either u e)", der Fehler wird also mit in die Monade geliftet. alfIo
und alcIo sind entsprechend umgebaute Abschlie�er.
-}
argErrorIo :: Either (IO a) e
-> IO (Either a e)
argErrorIo ei =
case ei
of Left io -> io >>= return . Left
Right e -> return (Right e)
alfIo :: ArgrF g e f (IO v)
-> f
-> (g -> IO (Either v e))
alfIo argr f g = argErrorIo (alf argr f g)
{- alfIo = argErrorIo `komp3` alf -}
alcIo :: ArgrC g e f (IO v)
-> f
-> (g -> IO (Either v [e]))
alcIo argr f g = argErrorIo (alc argr f g)
{- alcIo = argErrorIo `komp3` alc -}
{--------------------------------------------------------------------------
Argumentleser
---------------------------------------------------------------------------
Diese Argumentleser sind parametrisch f�r verschiedene Leserschemata,
Grundtypen und Argumenttypen �berladen. Bei den Schemata ist durch
Instanzdeklarationen festgelegt, welche Argumentleser zusammen mit
welchen Schemata verwendet werden k�nnen.
-}
-- Argumentleser ohne weitere Angaben
-- alternativ: "class Arg argr2 where". Dann w�re arg parametrisch polymorph
-- in und t. {---}
class Arg argr2 t where
arg :: argr2 (t -> t') t'
-- Leser f�r Argumente mit Namen
class Argn argr2 t where
argn :: String -- Argumentname
-> argr2 (t -> t') t'
-- Leser f�r Argumente mit Voreinstellung ohne Namen
class Argd argr2 t where
argd :: t -- Voreinstellung
-> argr2 (t -> t') t'
-- Leser f�r Argumente mit Voreinstellung und Namen
class Argnd argr2 t where
argnd :: String -- Argumentname
-> t -- Voreinstellung
-> argr2 (t -> t') t'
-- Argumentleser f�r unbenannte Argumente des Typs String, der nicht
-- vorhandene in leere Strings �bersetzt. Ben�tigt keine �berladung, weil
-- er auf argd zur�ckgreift.
args :: (Argd argr2 String) => argr2 (String -> t') t'
args = argd ""
-- Argumentleser f�r benannte Argumente des Typs String, der nicht
-- vorhandene in leere Strings �bersetzt. Ben�tigt keine �berladung, weil
-- er auf argnd zur�ckgreift.
argns :: (Argnd argr2 String) => String -> argr2 (String -> t') t'
argns name = argnd name ""
-- Argumenleser f�r Argumente des Typs (Maybe t). t kann jeder mittels
-- GenParser parspare Typ sein. Dieser Argumentleser liefert Nothing bei
-- nicht vorhandenem Argument.
argm :: (GenParser t, GenExtractor g GenArgError, GenReader argr4)
=> argr4 g GenArgError (Maybe t -> t') t'
argm =
genReader
genExtractor (Just Nothing)
(Just . genParser) (Right (ArgFormatError "{---}"))
-- Argumenleser f�r benannte Argumente des Typs (Maybe t). t kann jeder
-- mittels GenParser parspare Typ sein. Dieser Argumentleser liefert
-- Nothing bei nicht vorhandenem Argument.
argnm :: (GenParser t, GenNameExtractor g GenArgError, GenReader argr4)
=> String
-> argr4 g GenArgError (Maybe t -> t') t'
argnm name =
genReader
(genNameExtractor name) (Just Nothing)
(Just . genParser) (Right (ArgFormatError "{---}"))
{--------------------------------------------------------------------------
Argumentschreiber
--------------------------------------------------------------------------}
(\.) :: (g -> a -> g)
-> (g -> r)
-> (g -> a -> r)
infixr 9 \.
(\.) fa fr g a = fr (fa g a)
class Argb g t where
argb :: g -> t -> g
class Argbn g t where
argbn :: String -> g -> t -> g
{- Die sollten Argumentschreiber mit Null Argumenten sein, doch es gibt
Probleme mit \. .
-- namenlose Konstante in den Grundwert
class Argcb g t where
argcb :: t -> g -> g
-- benannte Konstante in den Grundwert
class Argcbn g t where
argcbn :: String -> t -> g -> g
-}
instance GenWriter t => Argb Row t where
argb tup t = tup ++ [Just (genWriter t)]
instance GenWriter t => Argbn Query t where
argbn name query t = (name, genWriter t) : query
argb2 :: (Argb g t1, Argb g t2) => g -> t1 -> t2 -> g
argb2 = argb \. argb
argbn1 :: (Argbn g t1) => String -> g -> t1 -> g
argbn1 = argbn
argbn2 :: (Argbn g t1, Argbn g t2) => String -> String -> g -> t1 -> t2 -> g
argbn2 n1 n2 = argbn n1 \. argbn n2
argbn3 :: (Argbn g t1, Argbn g t2, Argbn g t3)
=> String -> String -> String -> String -> g -> t1 -> t2 -> t3 -> g
argbn3 n1 n2 n3 n4 = argbn n1 \. argbn n2 \. argbn n3
argbn4 :: (Argbn g t1, Argbn g t2, Argbn g t3, Argbn g t4)
=> String -> String -> String -> String -> g -> t1 -> t2 -> t3 -> t4 -> g
argbn4 n1 n2 n3 n4 = argbn n1 \. argbn n2 \. argbn n3 \. argbn n4
blq :: (Query -> r) -> r
blq f = f []
blt :: (Row -> r) -> r
blt f = f []
-- Kompositionen f�r mehrere Argumente - falls an den geschriebenen
-- Grundtyp etwas gebunden werden soll.
-- zur Erl�uterung:
komp4 :: (e -> f)
-> (a -> b -> c -> d -> e)
-> (a -> b -> c -> d -> f)
liftkomp k f w a = f `k` (w a)
komp1 = (.)
komp2 = liftkomp komp1
komp3 = liftkomp komp2
komp4 = liftkomp komp3
komp5 = liftkomp komp4
komp6 = liftkomp komp5
komp7 = liftkomp komp6
komp8 = liftkomp komp7
komp9 = liftkomp komp8
{--------------------------------------------------------------------------
Komponenten f�r generische Leser
--------------------------------------------------------------------------}
{--------------------------------------------------------------------------
Generische Argumentparser
--------------------------------------------------------------------------}
class GenParser t where
genParser :: String -> Maybe t
instance GenParser Int where
genParser = readInt
instance GenParser String where
genParser = Just
instance GenParser Bool where
genParser txt =
if txt' == "1" || txt' == "ja" || txt' == "true" then Just True
else if txt' == "0" || txt' == "nein" || txt' == "false" then Just False
else Nothing
where txt' = map toLower txt
-- Ganzzahl lesen. Sie darf ein von Leerzeichen umgeben sein. Der
-- ganze String mu� abgedeckt werden.
readInt :: String -> Maybe Int
readInt str =
case readSigned readDec str
of [] -> Nothing
[(x,r)] -> case dropWhile isSpace r
of "" -> Just x
_ -> Nothing
{--------------------------------------------------------------------------
Generische Argumentleser
---------------------------------------------------------------------------
Dies dient zum Bauen von Schema�bergreifenden generischen Argumenlesern.
Der genReader kapselt alle schemabh�ngigen Teile. Er nimmt weitere
generische Teile, die �blicherweise in anderen Gen...-Klassen definiert
sind, als Argumente.
Ein Schema, das die Standard-arg...-Leserkonstruktoren verwenden will,
kann einen genReader definieren, und dann bei den
Arg...-Instanzdeklarationen die generischen ..._gen-Leser verwenden.
Der R�ckfallwert kann typischerweise sowohl ein verpackter Wert vom Typ
t, der anstelle des nicht lesbaren Arguments verwendet wird, als auch
ein ebenso in r verpackter Fehlerwert sein.
Typparemeter:
argr Argumentleser-Schema, das den generischen Leser bereitstellt
r Ergebnistyp, verpackt das regul�re Ergebnis der transformierten
Funktion sowie eventuelle Argumentlesefehler
In t', dem "Rest", ist jedes Schema parametrisch polymorph. Im Grundtyp
g und dem Typ des gelesenen Arguments t ist genReader parametrisch
polymorph, weil die Funktionen zum Umgang
-}
{---} -- Wie wird Polymorphie hier verwendet???
{---} {- �ber Unterschied der folgenden zwei Definitionen klarwerden:
class GenReader argr2 where
genReader :: (g -> (Either String e, g)) -- Extrahierer
-> Maybe t -- ggF Extrahier-R�ckfallwert
-> (String -> Maybe t) -- Parser
-> Either t (String -> e) -- Pars-R�ckfallwert
-> argr2 (t -> t') t' -- Argumentleser f�r ein Argument
-}
class GenReader argr4 where
genReader :: (g -> (Either String e, g)) -- Extrahierer
-> Maybe t -- ggF Extrahier-R�ckfallwert
-> (String -> Maybe t) -- Parser
-> Either t (String -> e) -- Pars-R�ckfallwert
-> argr4 g e (t -> t') t' -- Argumentleser f�r ein Argument
{- Hilfsfunktion zum Bauen von genReader-Implementierungen, die das
wiederkehrende Lesen des Arguments durch Extrahierer und Parser
verpackt.
Wenn das Extrahieren fehlschl�gt, liefert der Extrahierer einen Fehler
des Typs e. F�r diesen Fall kann mit (Just x) ein Ersatzwert (des Typs t)
f�r das fertig extrahierte und geparste Argument angegeben werden.
Dieser wird anstelle des nicht extrahierbaren Arguments verwendet. Der
Extrahierfehler wird verworfen (auch wenn es sich nicht um einen "nicht
gefunden"-Fehler handelt).
Der Parser liefert keinen Fehlerwert, sondern nur Nothing im Falle eines
Parsfehlers. Als viertes Argument mu� angegeben werden, was in diesem
Fall geschehen soll. Mit (Left x) kann ein Ersatzwert angegeben werden.
Alternativ ist mit (Right f) eine Funktion String -> e m�glich, die aus
dem extrahierten String einen Parserfehler erzeugt.
Wie ein beim Lesen des Arguments aufgetretener Fehler weiter behandelt
wird, ist Sache des Leserschemas. Es geschieht im vorletzten Argument
von bindReader. Das drittletzte gibt an, was mit einem erfolgreich
gelesenen Wert geschehen soll. Beidesmal zusammen mit dem
weitergegebenen Grundwert.
Die gebundenen Funktionen (valf und errf, zusammen mit dem Extrahierer)
bestimmen die Typen g und e. bindReader wird in g und e so polymorph,
wie diese Funktionen es sind.
-}
bindReader
:: (g -> (Either String e, g)) -- Extrahierer
-> Maybe t -- ggF Extrahierer-R�ckfallwert
-> (String -> Maybe t) -- Parser
-> Either t (String -> e) -- Parser-R�ckfallwert
-> g -- gelesener Grundwert
-> (g -> t -> a) -- an gelesenen Wert gebundene Funktion
-> (g -> e -> a) -- an Lesefehler gebundene Funktion
-> a
bindReader extr extrfb parse parsefb g valf errf =
let (txt_or_err, g') = extr g
in case case txt_or_err
of Left txt -> case parse txt
of Just val -> Left val
Nothing -> case parsefb
of Left fbval -> Left fbval
Right mkerr -> Right (mkerr txt)
Right err -> case extrfb
of Just val -> -- verwerfe Extrahiererfehler, verwende
-- R�ckfallwert
Left val
Nothing -> Right err
of Left val -> valf g' val
Right err -> errf g' err
{--------------------------------------------------------------------------
Generische Argumentschreiber
--------------------------------------------------------------------------}
class GenWriter t where
genWriter :: t -> String
instance GenWriter String where
genWriter = id
instance GenWriter Int where
genWriter = show
instance GenWriter Bool where
genWriter = show
{--------------------------------------------------------------------------
Generische Extrahierer
---------------------------------------------------------------------------
Extrahierer dienen zum Entnehmen von Einzelargumenten aus Grundwerten.
Diese werden verwendet, um generisch Argumentleser zu bauen. Der
Grundwert wird ggF abge�ndert und in ge�nderter Form an den n�chsten
Argumentleser weitergegeben. Das gilt auch, wenn kein Argument
extrahiert werden konnte (weil z.B. kein passendes gefunden wurde).
Extrahierer nehmen keine R�ckfallwerte, weil der extrahierte String noch
geparst werden mu�. Einen String als R�ckfallwert zu �bergeben, w�re
nicht sehr sinnvoll. Das hei�t aber, da� eine Funktion, die generische
Extrahierer verwendet, nur zwischen "Erfolg" und "irgendeinen Fehler"
unterscheiden kann. Andernfalls m��te der Fehler, je nach Fehlertyp
anders, untersucht werden. Die arg..._gen-Funktionen mit Extrahier-
-R�ckfallwert k�nnen diesen also nur bei jeglichem Extrahierfehler
einsetzen. Sie k�nnen sich damit nicht auf einen "nicht gefunden"-Fall
beschr�nken.
-}
{- Klasse von Grundtypen mit zugeh�rigem Fehlertyp f�r generische
Extrahierer nach Name. Diese Extrahierer verwenden einen Argumentname um
das Argument zu identifizieren. -}
class GenNameExtractor g e where
genNameExtractor :: String -- Name
-> g -- Grundwert
-> (Either String e, -- Inhalt, falls erfolgreich
g) -- weitergegebener Grundwert
{- Klasse von Grundtypen mit zugeh�rigen Fehlertypen f�r generische
Extrahierer eines "ersten" Arguments -}
class GenExtractor g e where
genExtractor :: g -> (Either String e, -- Inhalt falls erfolgreich
g) -- Weitergegebener Grundwert
{--------------------------------------------------------------------------
Generische Argumentlesefehler
---------------------------------------------------------------------------
Dies ist ein generischer Typ f�r einzelne Fehler beim Lesen einer
Argumentliste. Die Festlegung einer Fehlerbehandlungsstrategie ist Sache
des Schemas. F�r spezielle Fehlertypen gibt es Konvertierungsfunktionen
nach GenArgError. Das erm�glicht es, Argumentleser polymorph im Grundtyp
zu halten, wo genauere Einzelheiten nicht ben�tigt werden.
ArgError ist f�r Fehler beim Auffinden eines einzelnen Arguments, z.B.
Fehlen eines Arguments mit bestimmtem Name. ArgListError ist f�r Fehler,
die nicht einem einzelnen Argument zugeordnet werden k�nnen, z.B.
�berz�hlige Argumente oder fehlende Argumente beim Grundtyp Row.
ArgFormatError ist f�r Parsfehler.
Der String zur Angabe, bei welchem Argument der Fehler auftrat, sollte
eine Indentifizierung enhalten, wie Argumentnummer oder -name, so da�
ein Text "Fehler in Argument ...: ..." grammatisch korrekt wird.
-}
data GenArgError = ArgError -- Fehler bei einzelnem Argument
String -- welches Argument
String -- Beschreibung des Fehlers
| ArgListError -- Fehler in der Argumentliste als Ganze
String -- Beschreibung des Fehlers
| ArgFormatError -- Parsfehler bei einem Argument
String -- welches Argument
String -- Beschreibung des Fehlers
deriving Show {---}
{-
class ToGenArgError e where
toGenArgError :: e -> GenArgError
instance ToGenArgError GenArgError where
toGenArgError = id
-}
-- Dies stellt eine Standardfunktion bereit, um aus einem (mit genParser)
-- nicht parsbaren String einen Parserfehler des Typs e herzustellen. Wird
-- von arg_gen und argn_gen verwendet.
class GenParseError e where
genParseError :: String -> e
instance GenParseError GenArgError where
genParseError str =
ArgError "" {---}
("Fehler beim Parsen von \"" ++ str ++ "\"")