Hi.


ghc -fallow-undecidable-instances -fglasgow-exts -fno-prune-tydecls -O2 \
-c -o Arglib.o Arglib.hs

panic! (the `impossible' happened):
        mkWWcpr: not a product
    g{-rku-}
    -> (g{-rku-},
        PrelMaybe.Either{-r8t,i-}
            ((t{-rkv-} -> tzq{-rkT-}) -> tzq{-rkT-})
            [Arglib.GenArgError{-r2p,x-}])

Please report it as a compiler bug to [EMAIL PROTECTED]


bye
module Arglib (
   -- Grundtypen
   Query, Row,
   -- Argumentleser-Schemata
   Argr(..), ArgrF, ArgrC, alf, alc,
   -- Argumentleser
   Arg(..), Argn(..), Argd(..), Argnd(..),
   args, argns, arg_gen, argn_gen, argd_gen, argnd_gen,
   -- Argumentschreiber
   (\.), Argb(..), Argbn(..),
   argb2, argbn1, argbn2, argbn3, argbn4, blq, blt,
   -- 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 "missing")

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{---}),  [])
   genExtractor (Just str:xs) = (Left  str, xs)

instance GenExtractor Row GenArgError where
   genExtractor []            = (Right (ArgListError "zu wenige Attribute"),  [])
   genExtractor (Nothing:xs)  = (Right (ArgError "{---}" "fehlt"),  [])
   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.

newtype 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

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) => 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.

newtype 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

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) => 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 "{---}"))

argd_gen :: (GenParser t, GenExtractor g GenArgError, GenReader argr4) 
         => t -> argr4 g GenArgError (t -> t') t'
argd_gen def = 
   genReader genExtractor (Just def) genParser (Left def)

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 (Left def)


{--------------------------------------------------------------------------
                                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


{--------------------------------------------------------------------------
                               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 ""


{--------------------------------------------------------------------------
                             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 n1 = argbn n1

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 []


{--------------------------------------------------------------------------
                     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

-- 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


{--------------------------------------------------------------------------
   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 ++ "\"")

Reply via email to