Функциональное программирование

Функциональное программирование: > функции -- это тоже данные. `Int -> Bool` (функция) -- ровно такие же данные как и `Int`. Их также можно складывать в структуры, передавать как аргументы и т. п.

нотация

                  int x;
                
                  x :: Int
                
                  int f(int x)
                
                  f :: Int -> Int
                
                  static <T> T f(T x)
                
                  f :: a -> a
                
                  static <T> T f(T x)
                
                  f :: a -> a
                
                  f(x);
                
                  f x
                
                  f(x, y);
                
                  f x y
                
                  f x y = (f x) y
                
                  static <T> T f(T x)
static <T> T g(T x)
                
                  f :: a -> a
g :: a -> a
                
                  f(g(x));
g(f(x));
                
                  f (g x)
g (f x)
                

Композиция

f :: a -> a
g :: a -> a
f (g x)
(f . g) x == f (g x)
h = f . g
h x == f (g x)
f :: a -> a
g :: a -> a

h :: a -> a
h = f . g

моноид

Моноид

Бинарная асоциативная операция

(.) :: (a -> a) -> (a -> a) -> (a -> a)

Нейтральный элемент

id :: a -> a
id x = x

Haskell

Чисто функционаьлный

Все что функция может сделать -- посмотреть на свои аргументы это вернуть значение.

Referential transparency

(Любую константу можно заменить на само значение)
int x = 8;
int y = x++;
System.out.println(y + " might be the same as " + y);
int x = 8;
System.out.println(x++ + " might be the same as " + x++);

Нет переменных, циклов и условных переходов

Зато есть константы и нормальная рекурсия (А переходов вообще нет)

Очень ленивый

xs = [4, 1, 3, 2]
xs' = sort xs
print xs

`xs'` не нужен чтобы распечатать `xs`, поэтому сортироваться ничто не будет. (Зачем делать то, что можно не делать)

Если хотите быстро и просто потыкаться, тут есть интерктивная штука:

haskell.org

Синтаксис

Функции

add2 :: Int -> Int
add2 x = x + 2

Все функции и константы всегда обозначаются словами с маленькой буквы без пробелов.

(Константы это просто функции с нулем аргументов.)

Pattern mathcing

fixBuz :: Int -> String
divide8 3 = "Fiz"
divide8 5 = "Buz"
divide8 15 = "FizBuz"
divide8 _ = "Some other number"

Так матчить можно произвольные структуры произвольного уровня вложенности. `_` -- специальное название константы, которое говорит что вам все равно что в ней лежит.

Структуры

data Foo = Bar
foo :: Bar
foo = Bar

`Foo` -- тип структуры. `Bar` -- конструктор структуры. Тут у `Foo` всего одно значение `Bar`.

Произведение типов

(обычные поля структур)
data PersonType = Person String Int
vasya :: PersonType
vasya = Person "Vasya" 8
-- тип можно не укзывать
petya = Person "Petya" 5
getName :: PersonType -> String
getName (Person name _) = name
greetPerson :: PersonType -> String
greetPerson p = "Hello, " ++ getName p
greetPerson petya
-- "Hello, Petya"

Еще немного функций

greetPerson :: PersonType -> String
greetPerson p = "Hello, " ++ getName p
greetPerson :: PersonType -> String
greetPerson p = "Hello, " ++ name
  where
    name = getName p
greetPerson :: PersonType -> String
greetPerson p = "Hello, " ++ name
  where
    getName' (Person name _) = name
    name = getName' p
data PersonType = Person String Int

getName :: PersonType -> String
getName (Person name _) = name
greetName :: String -> String
greetName name = "Hello, " ++ name
greetPerson :: PersonType -> String
greetPerson p = greetName (getName p)
greetPerson :: PersonType -> String
greetPerson = greetName . getName
greetPerson petya
-- "Hello, Petya"
(greetName . getName) petya
-- "Hello, Petya"

Суммы типов

data Bool = False | True
x :: Bool
x = True
y = False
ifThenElse :: (Bool, a, a) -> a
ifThenElse (True, a, _) = a
ifThenElse (False, _, b) = b
ifThenElse (True, "Hello", "World")
-- "Hello"
ifThenElse (False, "Hello", "World")
-- "World"
data CircleType = Circle Double Double Double
data RectangleType = Rectangle Double Double Double Double
data Shape =
  CircleShape CircleType | RectangleShape RectangleType
surface :: Shape -> Double
surface (CircleShape (Circle _ _ r)) =
  pi * r ^ 2
surface (RectangleShape (Rectangle x1 y1 x2 y2)) =
  (abs (x2 - x1)) * (abs (y2 - y1))
shape = CircleShape (Circle 0 0 2)
surface shape
-- 12.566370614359172
otherShape = RectangleShape (Rectangle 1 2 3 4)
surface otherShape
-- 4.0

И еще немного функций

Лямбда-выражения

add8 :: Int -> Int
add8 x = x + 8
add8 = \x -> x + 8

λ -- `\` (λ печатать тяжело)

foo :: (Int -> Int) -> Int
foo add8
foo (\x -> x + 8)

Давайте придумаем синтаксис для функции нескольких аргументов!

x, y :: Int
x = 42
y = 69
xPlusY :: Int
xPlusY = add x y

Применение функции -- лево-ассоциативно

xPlusY = (add x) y
xPlusY = f y
f = add x
f :: Int -> Int
add :: Int -> (Int -> Int)
add :: Int -> (Int -> Int)

Тип `->` -- право-ассоциативный

add :: Int -> Int -> Int
add a b = a + b
add = \a b -> a + b

Любая функция берет строго один аргумент.

Функция нескольких аргументов все равно берет строго одтн аргумент и возвращает функцию, которая берет следйющий.

(И из-за того, что применение функции лево-ассоциативно, вызов таких не трубует особого синтаксиса.)

Currying

add :: Int -> Int -> Int
add a b = a + b
add8 :: Int -> Int
add :: Int -> (Int -> Int)
add8 = add 8
add8 3
-- 11

Funny fact

Оператор (например `+`) -- функция, название которой не содержит буквы и цифры.

x +&+ y = x + y
8 +&+ 9
-- 17

Funny fact 2

Оператор можно превратить в функцию, окружив его скобками.

add :: Int -> Int -> Int
add x y = x + y
add = (+&+)
add = (+)

Funny fact 3

Функцию можно превратить в оператор, окружив ее обратными кавычками.

add :: Int -> Int -> Int
add x y = x + y
add 8 9
-- 17
8 `add` 9
-- 17

Список

Односвязный список

data IntList = Cons Int IntList | Nil
nums :: IntList
nums = 1 `Cons` (2 `Cons` (3 `Cons` Nil))
sum :: IntList -> Int
sum (Cons x xs) = x + sum xs
sum Nil = 0
sum (Cons x xs) = x + sum xs
sum nums
-- 6
take :: Int -> IntList -> IntList
take _ Nil = Nil
take 0 _ = Nil
take n (Cons x xs) = Cons x (take (n - 1) xs)
nums :: IntList
nums = 1 `Cons` (2 `Cons` (3 `Cons` Nil))
take 2 nums
-- Cons 1 (Cons 2 Nil)
take 1029 nums
-- Cons 1 (Cons 2 (Cons 3 Nil))
take 0 nums
-- Nil
repeat :: Int -> IntList
repeat n = n : (repeat n)
repeat 8
-- Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (...
(take 3 . repeat) 8
-- Cons 8 (Cons 8 (Cons 8 Nil))
(sum . take 3 . repeat) 8
-- 24
Наша самодеятельность В стандартной библиотеке
IntList
[Int]
Nil
[]
Cons
:
Cons 3 (Cons 4 Nil)
3 : 4 : []
[3, 4]

`repeat`, `sum` и `take` тоже есть в стандартной библиотеке.

QuickSort

quicksort :: [Int] -> [Int]
quicksort (x:xs) =
  quicksort smaller ++ [x] ++ quicksort larger
  where
    smaller = filter (< x) xs
    larger = filter (>= x) xs
filter :: (Bool -> Int) -> [Int] -> [Int]
filter f (x:xs) =
  if f x
    then filter f xs
    else x:(filter f xs)
filter _ [] = []
filter f (x:xs) =
  if f x
    then filter f xs
    else x:(filter f xs)
quicksort :: [Int] -> [Int]
quicksort (x:xs) =
  quicksort smaller ++ [x] ++ quicksort larger
  where
    smaller = filter (< x) xs
    larger = filter (>= x) xs
    filter _ [] = []
    filter f (x:xs) =
      if f x
        then filter f xs
        else x:(filter f xs)
quicksort :: [Int] -> [Int]
quicksort [] = []
quicksort (x:xs) =
  quicksort smaller ++ [x] ++ quicksort larger
  where
    smaller = filter (< x) xs
    larger = filter (>= x) xs
    filter _ [] = []
    filter f (x:xs) =
      if f x
        then filter f xs
        else x:(filter f xs)
quicksort [2, 1, 3, 4]
-- [1, 2, 3, 4]

`filter` тоже есть в стандартной библиотеке.

Где и как смотреть "стандартную библиотеку"

  1. Hackage

    (Там вам нужен только пакет `base`. Ссылка ведет прямо на него.) Еще если там нажать `s`, то будет поиск.

  2. Hoogle

    Это поиск по типам. Например: `Int -> [Int] -> [Int]` (Тут вам опять же нужен только пакет `base`. Нужно чтобы справа было "package:base".)