Haskell – 34 – tipi di dati algebrici – 1

Continuo da qui, copio qui.

All the types that we used so far were either already provided by the standard Prelude, such as Float, Char, Double, or Int, or they were lists or tuples of these types. We also used type synonyms to define more convenient and descriptive names, as in

type Point = (Float, Float)
type Line = (Point, Point)

While type synonyms are convenient, they only associate new names with existing types. Sometimes we need entirely new types to be able to model data properly. For example, to create more varied graphics, we might want to extent the Line type with a line style; then, instead of always drawing solid lines, we can offer dotted and dashed lines as alternative styles. How can we represent this attribute? We might be tempted to use strings and define

type LineStyle = String
type FancyLine = (Point, Point, LineStyle)

and use it as in

myLine :: FancyLine
myLine = ((0, 0), (1, 1), "dashed")

Unfortunately, this has a number of serious drawbacks. For one, it’s easy to introduce mistakes that will lead to unpredictable behaviour — we might accidentally write

myLine :: FancyLine
myLine = ((0, 0), (1, 1), "bashed")

which (as far as the compiler is concerned) conforms to the type definition. After all, "bashed" is a String, even if it is not a valid line style. Consequently, each function using the line styles has to check —at runtime— if the string is a valid style. Here is an example of such a function (where the function elem checks if an item is an element of a given list):

changeLineStyle :: FancyLine -> LineStyle -> FancyLine
changeLineStyle (x, y, _) newStyle
  | newStyle `elem` ["solid", "dashed", "dotted"] = (x, y, newStyle)
  | otherwise 
  = error $ "error in changeLineStyle: " ++ newStyle ++ " is not a valid style"

This is unsatisfactory! It is a hassle to implement all the error checking — and easily forgotten, too. It slows down code execution (string comparison is a relatively time consuming operation), but worst of all: invalid line styles cannot be spotted by the compiler before we run the program; instead, we get runtime errors.

A much better approach is to introduce a new data type that comprises exactly the three admissible line style value, so that the compiler can reject any program that attempts to use an illegal value. In Haskell, the keyword data introduces the definition of a data type. It is followed by the name of the new type and its values (separated by | characters), as in

data LineStyle
  = Solid
  | Dashed
  | Dotted

The identifiers representing the elements of the new type (here, Solid, Dashed, and Dotted) are called the data constructors of type LineStyle. Just as type names, they must start with an uppercase letter. Now, we can define

myLine :: FancyLine
myLine = ((0, 0), (1, 1), Dashed)

and, if we misspell (ho raccolto il codice in msp.hs),

Prelude> :l msp
[1 of 1] Compiling Main             ( msp.hs, interpreted )

msp.hs:12:27: error:
    • Data constructor not in scope: Bashed :: LineStyle
    • Perhaps you meant ‘Dashed’ (line 4)
Failed, modules loaded: none.

the compiler will be able to assist us by pointing out that the data constructor Bashed is unknown. If we accidentally start the data constructor with a lower case letter, as in

Prelude> myLine = ((0,0), (1, 1), dashed)

:3:26: error: Variable not in scope: dashed

the compiler will treat it as a variable and report that dashed is not in scope. Moreover, the check for a runtime error in our previous definition of changeLineStyle is now obsolete and we can simply write (file cst.hs)

data LineStyle
  = Solid
  | Dashed
  | Dotted

type Point = (Float, Float)
type Line  = (Point, Point)

type FancyLine = (Point, Point, LineStyle)

changeLineStyle :: FancyLine -> LineStyle -> FancyLine
changeLineStyle (x, y, _) newStyle = (x, y, newStyle)
Prelude> :l cst
[1 of 1] Compiling Main             ( cst.hs, interpreted )
Ok, modules loaded: Main.

All around, a better solution!


Posta un commento o usa questo indirizzo per il trackback.



Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d blogger hanno fatto clic su Mi Piace per questo: