Module Error defines Err type that is used in the interpreter.
> module Error where
>
> import qualified Text.ParserCombinators.Parsec as P
> import qualified Text.PrettyPrint.HughesPJ as PP
> import Text.PrettyPrint.HughesPJ ( (<>), (<+>), ($$), ($+$) )
> import qualified Control.Monad.Error as E
>
> import LangData
>
> type ErrMsg = String
> data Err = Err P.SourcePos ErrMsg | Default ErrMsg
Err in data Err is a type constructor. Err after = is a data constructor. Type constructor is used to construct a type (in this case type Err). And, data constructor is used to construct a value or data of some type. So, a value of type Err can be constructed using Err data constructor. Default is another data constructor that can be used to create a value of type Err.
> ppErr (Err pos msg) = PP.fsep [PP.text (show pos), PP.text msg]
> ppErr (Default msg) = PP.text msg
ppErr turns a value of type Err into a value that can be pretty-printed (looks nice when printed).
> instance Show Err where
> show e = PP.render $ ppErr e
By making Err an instance of Show class, a value of type Err can be converted to String and printed. When a value of type Err is converted to String, ppErr is used to make the string representation look nice.
> instance E.Error Err where
> noMsg = Default "Error"
> strMsg = Default
By making Err an instance of Error class, Err can be used wherever an instance of Error class can be used.
Haskell has notion of typeclasses.
A typeclass can be declared using class keyword:
class Foo a where
bar :: a -> Int
foo :: a -> String
The above declares a typeclass Foo that provides 2 functions: bar and foo. All instances of Foo should implement those 2 functions:
data Bar = Bar Int String
instance Foo Bar where
bar (Bar i _) = i
foo (Bar _ s) = s
Bar is an instance of Foo and it implements bar and foo. A value of type Bar can be used anywhere where a value of an instance of class Foo is expected:
func :: (Foo a) => a -> (String, Int)
func a = (foo a, bar a)
func (Bar 1 "1")
==> ("1", 1)
func :: (Foo a) => a -> (String, Int) means that the function func has type a -> (String, Int) where a is an instance of Foo (an instance of a class is a type).
If there were another instance of Foo, it could be passed to func:
data A = A
instance Foo A where
bar _ = 1
foo _ = "1"
func A
==> ("1", 1)
So, with typeclass, one can overload a function. func is overload to take a value of type A or Bar (both are instances of class Foo).