Haskell – 5 – elementi di Haskell – 5

Continuo da qui, copio qui, scrollare fino a “Infix and Prefix Application”.

Applicazioni infisse e prefisse
Functions like + and * are binary functions, that is, functions which expect exactly two arguments, just like our average function:

average a b = (a + b) / 2.0

When we want to apply average, we first write the function name and then the arguments

Prelude> average 6.9 7.25
7.075

while with addition and multiplication, we place the function in-between the arguments:

Prelude> 1 + 5
6
Prelude> 3.4 * 7.2
24.48

We call the former notation prefix, as the function appears before the arguments, and the latter infix, as it is in-between its argument. We can easily convert an infix function into a prefix one by simply placing it in parenthesis:

Prelude> (+) 1 5
6
Prelude> (*) 3.4 7.2
24.48

and conversely, we can use a regular binary function as infix operator by placing its name between backquotes:

Prelude> 6.9  `average` 7.25
7.075

Nota: ` con Linux lo ottengo con AltGr’ con Windows non lo so.

Binary functions in backquotes are, by default, left associative, this means that multiple applications, as in

Prelude> 6.9 `average` 7.25 `average` 3.4
5.2375

are implicitely grouped to the left; so, the above expression is the same as

Prelude> (6.9 `average` 7.25) `average` 3.4
5.2375

Which notation you use is a matter of style. In general, people can parse arithmetic expressions much more easily if they are in infix notation.

Una prima occhiata all’overloading
All serious programming languages provide some functions whose argument types are not restricted to a single type, but instead a whole family of types is admitted. For example, both 1 + 2 (where the arguments are of type Int) as well as 1.5 + 1.2 (where the arguments are of type Float) make sense. Consequently, the function + simultaneously has the type

(+) :: Int -> Int -> Int

as well as the type

(+) :: Float -> Float -> Float

We call functions, such as +, overloaded functions; the name of an overloaded function carries more than just one meaning as witnessed by the multiplicity of type signatures. The motivation for permitting overloaded functions, such as +, is that it would be awkward to enforce the use of two different symbols —that is, two different function names— for the two cases of adding integers or adding floating-point numbers. (Note that Haskell requires us to use the prefix notation of an operator in a type signature; i.e., we write (+) and not just +.)

Unfortunately, all of this means that, given our current knowledge, we cannot denote the type of + in a single type signature of the form (+) :: type; instead, we have to resort to a family of type signatures (one for each possible type of +). To improve on this, we need to consider additional notation, where we exploit the fact that both argument types and the result type in one particular use of + are always identical. In other words, we might say that + has type a -> a -> a where a is either Int or Float. In fact, Haskell does not restrict a to only Int or Float, but instead allows any numeric type (most of which we have not encountered yet). We denote the set of numeric types by Num and generally call such sets of types type classes.

Using the type class Num, we can specify the type of + to be a -> a -> a, where the type a is a member of set Num, or, using mathematical notation a ∈ Num. Haskell abbreviates a ∈ Num to Num a and places it in front of the function type separated by a double arrow =>. Hence, the closed form of the type signature for + is

(+) :: Num a => a -> a -> a

Other binary arithmetic operations, such as - and *, have the same type. Note how types, such as Int and Float, as well as type classes, such as Num, have names starting with an upper case letter, whereas place holders, such as a, have names starting with a lower case letter. This convention simplifies reading type signatures and is enforced in Haskell. We call place holders in types, such as a above, type variables. They are important in programming languages that have a sophisticated type system.

In addition to Num, another important type class is Eq. It contains all those types for which the function == is defined, which checks whether its two arguments are equal. All types that we have encountered so far, except function types, are part of Eq. So, all of the following make sense:

Prelude> 2 == 2
True
Prelude> 5.0 == 6.0
False
Prelude> ("Hello " ++ "World!") == "Hello World!"
True

The type of == is

(==) :: Eq a => a -> a -> Bool

where Bool is the type of Boolean values False and True.

Another important type class is Show. It contains all types for which the system knows how to convert them to a string representation, and the most important function of this class of values is the show function:

Prelude> show 123
"123"
Prelude> show 1.75
"1.75"
Prelude> show False
"False"
Prelude> show "False"
"\"False\""

It is important to note that the Int value 123 and the string "123" are two fundamentally different objects, same for the boolean value False and the string "False". As we can see in the last example, if we apply show to a value which is already a string, it returns a different string, containing the opening and closing quotes as additional characters.

Functions are not in the type class Show, so if we try and apply show to the function inc, for example, the compiler will complain:

Prelude> inc x = x + 1
Prelude> show inc

:18:1: error:
    • No instance for (Show (Integer -> Integer))
        arising from a use of ‘show’
        (maybe you haven't applied a function to enough arguments?)
    • In the expression: show inc
      In an equation for ‘it’: it = show inc

The compiler message No instance for (Show (Int -> Int)) means that the type Int -> Int is (so far) not in in the type class Show. We will later see that we can extend type classes, and the compiler suggests to do so as a fix

The function show is also invoked whenever we enter an expression […] at the GHCi prompt: after the expression is evaluated, the system tries to convert it to a string using the show function, so it can print it. Therefore, if we just enter a function […] at the GHCi prompt, we will see a similar error message. All this is illustrated in the following screencast. Ahemmm… il video si può vedre di là. Ma i preferisco il testo; prossimamente… 😉

Frequently used type classes and overloaded functions. We will cover type classes and overloading in more detail in later chapters. For now, here is an overview of some frequently used type classes, and some overloaded operations on these type classes.

Typeclass Show

  • functions: show :: Show a => a -> String: convert the given value into a string.
  • member types: almost all predefined types, excluding function types.

Typeclass Eq

  • functions: (==), (/=) :: Eq a => a -> a -> Bool: equality and inequality.
  • member types: almost all predefined types, excluding function types.

Typeclass Ord

 

  • functions: (<), (>), (<=), (>=) :: Ord a => a -> a-> Bool: less than, greater than, less or equal, greater or equal
  • member types: almost all predefined types, excluding function types.
  • all types in Ord are already in Eq, so if you are using both == and < on a value, it is sufficient to require it to be in Ord.

Typeclass Num

  • functions: (+), (-), (*) :: Num a => a -> a -> a: arithmetic operations.
  • member types: Float, Double, Int, Integer.

Typeclass Integral

  • functions: div, mod :: Integral a => a -> a -> a: division.
  • member types: Int (fixed precision), Integer (arbitrary precision)

Typeclass Fractional

  • functions: (/) :: Fractional a => a -> a -> a: division.
  • member types: Float, Double

Typeclass Floating

functions: sin, cos, tan, exp, sqrt , ... :: Floating a => a -> a: trigonometric and other functions.

member types: Float, Double

We will introduce more type classes and operations as we use them. If you want to find out more about a type class, select its name and type :info TYPECLASS-NAME in GHCi.

Prelude> :info Integral
class (Real a, Enum a) => Integral a where
  quot :: a -> a -> a
  rem :: a -> a -> a
  div :: a -> a -> a
  mod :: a -> a -> a
  quotRem :: a -> a -> (a, a)
  divMod :: a -> a -> (a, a)
  toInteger :: a -> Integer
  {-# MINIMAL quotRem, toInteger #-}
  	-- Defined in ‘GHC.Real’
instance Integral Word -- Defined in ‘GHC.Real’
instance Integral Integer -- Defined in ‘GHC.Real’
instance Integral Int -- Defined in ‘GHC.Real’

Nota: ho eliminanto i riferimenti al Mac, non frequento macisti 😜

🤢

Posta un commento o usa questo indirizzo per il trackback.

Trackback

Rispondi

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

Logo di 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...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

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