Hier die Zusammenfassung der ersten Hälfte des dritten Kapitels von "Real World Haskell". Außerdem haben wir ein paar Compiler-Fehlermeldungen besprochen und analysiert wie sie auf den konkreten Fehler hindeuten.
12 data MyType
=
MyConstructor
Int
String
deriving(Show)
1234 ghci>MyConstructor
1
"me"
ghci>:
type
it
it :: MyType
1 type
MyId
=
Int
1 data MyType
=
MyFirstConstructor | MySecondConstructor
Um aus einem vordefinierten Datentyp oder Tupel einzelne Werte zu extrahieren
kann mit einer entsprechenden Funktionsdefinition der Typus 'dekonstruiert' werden:
12345 getID :: MyType
-
>
Int
getID (MyConstructor
id
_)
=
id
getName :: MyType
-
> String
getName (MyConstructor _ name)
=
name
Da das Wunschdatum von seiner Position im Datentyp abhängt, müssen die Variablen
entsprechend der Reihenfolge im Constructor definiert werden. Sollten bestimmte
Variablen nicht interessieren, kann der Platzhalter _ auch mehrfach benutzt werden,
um Variablen zu ignorieren.
Mit dem : lassen sich Listen im Funktionsaufruf dekonstruieren:
12 mySum []
=
0
mySum (x:xs)
=
x
+
mySum xs
Die Record Syntax ist eine convienience-Schreibweise, mit der neben der
Typen-Deklaration auch automatisch die Getter der einzelnen Typ-Komponenten erstellt werden
können:
1234567 data MyOtherType
=
MyOtherType {
myId :: Integer
, name :: String
} deriving (Show)
ghci> name (MyOtherType
5
"my name"
)
"my name"
Hier sei erwähnt, dass die Namensräume für Typen und für Funktionen getrennt sind, so dass es nicht nur kein Problem darstellt, den Konstruktor zu nennen wie den Typen, es ist obendrein auch gängige Praxis in der Haskell-Welt.
Parametrisierte Typen legen ihre Parameter nicht auf einen Basistypen fest sondern können
ähnlich wie Listen und Tupel unterschiedliche Datentypen aufnehmen.
1234567891011121314151617181920 mySum :: Num a
=
> [a]
-
> a
mySum []
=
0
mySum (n:ns)
=
n
+
mySum
ghci> :load mySum.hs
[
1
of
1
] Compiling Main ( mySum.hs, interpreted )
mySum.hs:
13
:
20
:
Could
not
deduce (a ~ ([Integer]
-
> Integer))
from
the context (Num a)
bound by the
type
signature
for
mySum :: Num a
=
> [a]
-
> a
at mySum.hs:
11
:
10
-
26
`a'
is
a rigid
type
variable bound by
the
type
signature
for
mySum :: Num a
=
> [a]
-
> a
at myLength.hs:
11
:
10
In the second argument of `(
+
)
', namely `mySum'
In the expression: n
+
mySum
In an equation
for
`mySum': mySum (n : ns)
=
n
+
mySum
Failed, modules loaded: none.
ghci>
Hier war der Fehler, dass mySum rekursiv ohne ns, also ohne Liste aufgerufen wurde. Die entscheidenden Zeilen aus der Kompiler-Fehlermeldung weisen darauf hin, dass der zweite Parameter der Funktion (+)
den Typen einer Funktion aufweist und nicht den eines Nums, wie es erwartet wird:
1234 `a'
is
a rigid
type
variable bound by
the
type
signature
for
mySum :: Num a
=
> [a]
-
> a
at myLength.hs:
11
:
10
In the second argument of `(
+
)
', namely `mySum'
123456789101112131415161718192021 myMean :: Num a
=
> [a]
-
> Double
myMean ns
=
fromIntegral(
sum
(ns))
/
fromIntegral(length(ns))
ghci> :load myMean.hs
[
1
of
1
] Compiling Main ( myMean.hs, interpreted )
myMean.hs:
19
:
13
:
Could
not
deduce (Integral a) arising
from
a use of `fromIntegral'
from
the context (Num a)
bound by the
type
signature
for
myMean :: Num a
=
> [a]
-
> Double
at myMean.hs:
18
:
11
-
32
Possible fix:
add (Integral a) to the context of
the
type
signature
for
myMean :: Num a
=
> [a]
-
> Double
In the first argument of `(
/
)
', namely `fromIntegral (sum (ns))'
In the expression:
fromIntegral (
sum
(ns))
/
fromIntegral (length (ns))
In an equation
for
`myMean':
myMean ns
=
fromIntegral (
sum
(ns))
/
fromIntegral (length (ns))
Failed, modules loaded: none.
ghci>
Die Fehlermeldung besagt lediglich, dass im ersten Parameter der (/)-Funktion fromIntegral mit einem nicht Integral-Typen aufgerufen wird. Der eigentliche Fehler ist aber etwas versteckter, da die Funktion ohne Typen-Deklaration funktionierte
aber dann folgende Typensignatur inferiert wurde:
1 myMean :: (Fractional a, Integral a1)
=
> [a1]
-
> a
Wie kommt diese Signatur zu Stande, wo doch für sum eine Liste von Num erwartet wird:
12 ghci> :t
sum
sum
:: Num a
=
> [a]
-
> a
und für length der eigentliche Typus der Liste überhaupt keine Rolle spielt:
12 *
Main> :t length
length :: [a]
-
>
Int
? Hier wird fromIntegral als "Casting"-Funktion eingesetzt um aus einem Integral-Type ein
Num zu machen. In herkömlichen Sprachen würde man erwarten, dass eine Casting-Funktion irgend
einen Typ in den Typ konvertiert nach dem sie benannt ist. In Haskell fordert fromIntegral, dass
ihm ein Integral übergeben wird. Das bedeutet, dass fromIntegral(sum xs) das eher ungezwungenere sum
auffordert ihm Integral zu liefern und schränkt dadurch den Typenbereich den sum sonst überdecken würde ein.
Die richtige Lösung lautet daher, fromIntegral für den erste Operanden von (/) nicht zu verwenden:
12 myMean :: Fractional a
=
> [a]
-
> a
myMean (xs)
=
(
sum
xs)
/
fromIntegral( length xs)
So kann man mit Fractionals arbeiten, die diverse Literale erlauben:
1234567 ghci> myMean [
1
,
2
,
3
]
2.0
ghci> myMean [
1.2
,
2.3
,
3.4
]
2.3
ghci> :module Data.Ratio
ghci> myMean [
1
%
2
,
3
%
4
,
5
%
4
]
5
%
6