
Hi Habr! Today I took out my old notes on the course “Haskell as the first programming language” by Sergei Mikhailovich Abramov and I will try to communicate as clearly as possible and with examples about this wonderful language to those who are not familiar with it. The story is focused on the unprepared reader. So even if you first heard the word Haskell ...
The second part ofHaskell base types
The basic types of Haskell are:
Numbers
Boolean values
Characters
Lists
Ordered sets (tuples)
Functions
')
NumbersWhole:
Integer (-∞, ∞)
Int (-2 ^ 31, 2 ^ 31-1)
In the prelude (standard library) there are definitely a lot of useful functions for integers, including conversion to a floating point number (fromInt and fromInteger)
Floating point numbers:
Float (7 decimal places)
Double (16 decimal places)
Boolean valuesBool (True | False)
Conjunction, disjunction, and negation operations (&&, ||, not)
CharactersChar ('a')
And Char functions in Int and Int in Char (ord, chr)
ListsLists can be different:
[Int] - list of integers [1,2,3,4]
[Char] - list of characters (string)
[[Int]] - array
[Float -> Float] is a list of functions.
etc.
Several standard operations in the examples:
Main> head [1,2,3]
one
Main> tail [1,2,3]
[2,3]
Main> length [True, False]
2
Main> reverse [1,2,3]
[3,2,1]
Main> 0: [1,2,3]
[0,1,2,3]
Main> - ghci compiler console prompt
":" - the operation of attaching an element to the list.
Ordered setsExamples:
(2.4, "cat") (Float, [Char])
('a', True, 1) (Char, Bool, Int)
([1,2], sqrt) ([Int], Float-> Float)
(1, (2, 3)) (Int, (Int, Int))
But, the heart of Haskell and all functional programming is, of course, the functions themselves!
Functions
The function, in modern mathematics, is the law of correspondence, which associates with each element x from this set A a single (or none) element y from set B.
Haskell, by its purpose, is, first of all, the language of mathematicians, so the syntax here corresponds as closely as possible to this definition.
Example:
square :: Integer -> Integer square x = x*x
As you probably guessed, this is a function of squaring a number. We analyze it in detail:
The first line is the function declaration:
Function_name :: definition_field -> value_field
square :: Integer -> IntegerIt should be said that in Haskell it is not necessary to always declare a function. In a number of cases, the interpreter will understand what areas of definition and value are for a given function. However, lowering ads - moveton.
The second line is the function definition:
Function_name Parameters = Calculation_Rule
square x = x * xThe function without parameters is nothing more than a constant:
e :: Float e = exp 1.0
Function with several parameters:Thanks to
janatem for the clarification (
read the comments ).
abcFormula :: Float -> Float -> Float -> [Float] abcFormula abc = [ (-b+sqrt(b*b-4.0*a*c))/(2.0*a), (-b-sqrt(b*b-4.0*a*c))/(2.0*a) ]
Function definitions with alternatives
As in any language, Haskell has branching constructs.
Let us analyze them using the example of the abs function (module).
If ... then ... else ... abs1 x = if x>=0 then x else -x
Case ... of ... abs2 x = case x>=0 of True -> x False -> -x
But, besides the standard if and case, Haskell has a very beautiful and most used branch design. The so-called
guard expressions . Example:
abs3 x | x>0 = x | x<0 = -x | otherwise = 0
The straight line should be read as: "when".
We read: "The function abs3, with the input parameter x, for x> 0 takes the value x, for x <0 takes the value -x, and in any other case takes the value 0".
Of course, we could write everything down using two guard expressions, but I wrote down three, so that it would be clear that there could be as many of them as possible.
Otherwise in the foreplay is defined very simply:
otherwise :: Bool otherwise = True
That is, you can safely write instead of “otherwise” “True”, but this, again, is a moveton.
Pattern matching
One of the most common and effective techniques in Haskell is pattern matching. Instead of a parameter, we can slip functions into an example of how the parameter should look. If the sample is approached, the function is executed; if not, it goes to the next sample. For example, defining factorial through recursion using patterns:
fact :: Integer -> Integer fact 0 = 1 fact n = n * fact (n-1)
The same, but with the help of security expressions:
fact :: Integer -> Integer fact n | n==0 = 1 | n>0 = n*fact (n-1)
There is a very common pattern for the list: (x: xs). X - denotes the first element, XS - the rest of the list (except the first element). ":" - the operation of adding an element to the list. Examples from foreplay:
head :: [a] -> a head (x:_) = x head [] = error "Prelude.head: empty list" tail :: [a] -> [a] tail (_:xs) = xs tail [] = error "Prelude.tail: empty list"
The head function takes as input a list of anything [a] and returns the first element of this list. The tail function takes as input a list of anything [a] and removes the first item from this list.
“_” - means that this element does not interest us.
Well, that's all for today. If there is interest, I will write a sequel soon.