Haskell Type System
Ивашнёв Дмитрийemail: [email protected]
jabber: [email protected]
25 апреля 2014 г.
Парадигма
• Функциональный язык (functional)• Чисто функциональный язык (purely functional)• Язык с ленивой моделью вычислений (lazy)• Язык со статической типизацией (static typing)• Язык со строгой типизацией (strong typing)
Функциональный язык (functional)
fact(n) =
{1 if n = 0n × fact(n − 1) otherwise
Функциональный язык (functional)
module Factwhere
fact 0 = 1fact n = n*fact (n-1)
Чисто функциональный язык (purelyfunctional)
Представьте что все const
Чисто функциональный язык (purelyfunctional)
f (x) = ax2 + bx + c
Решить для x при f (x) = 0
∆ = b2 − 4ac
x1,2 =−b ±
√∆
2a
Чисто функциональный язык (purelyfunctional)
module Discriminantwhere
solveQuadratic a b c =let d = b*b - 4*a*c
in if d < 0 then []else if d == 0 then [-b/(2*a)]else [-b + (sqrt d)/(2*a), -b - (sqrt d)/(2*a)]
Чисто функциональный язык (purelyfunctional)
d = b * b - 4 * a * c
Списки (lists)
• [] - пустой список• [1, 2, 42] - список с тремя элементами• [[], [1], [1, 2], [1, 2, 42]] - список списков
Списки (lists)
• [1, 2 .. 5] = [1, 2, 3, 4, 5]• [1, 2 .. ]• [1 .. ]
Списки (lists)
Добавление в список42:[][42]
8:[42][8,42]
Списки (lists)
Сложность операцийlength - O(n)Проверка на пустой список: nullНе используйте length ls == 0Привет C++ и .empty() из STL
Функции первого порядка
Функции могут передаваться в качестве параметров функцийodd 1True
filter odd [1, 2, 3][1, 3]
Язык с ленивой моделью вычислений(lazy)
filter odd [1, 2, 3, 4, 5]
filter odd [1, 2 .. 5]
[1, 3, 5]
Язык с ленивой моделью вычислений(lazy)
filter odd [1 ..]
take 3 (filter odd [1 ..])
[1, 3, 5]
Язык со статической типизацией (statictyping)
Проверка типов на этапе компиляции
Язык со строгой типизацией (strongtyping)
Явные приведения между всеми типамиInt, Float, ...
С чего начать
http://www.haskell.org/Компиляторhttp://www.haskell.org/ghc/
Математическая нотация
Множества A и BФункция F
F : A→ B
Нотация типов в Haskell
Переменные типов: a и bВ данном случае на типы не накладывается никакихограниченийФункция f
f :: a -> b
Примеры
Переменные типов: a, b и cФункция f
f :: a -> b -> c
Спискиf :: a -> [a]
Функции первого порядка
filter :: (a -> Bool) -> [a] -> [a]map :: (a -> b) -> [a] -> [b]foldr :: (a -> b -> b) -> b -> [a] -> b
Логика типов
Определение тела функции по ее типуf :: a -> a
Логика типов
Определение тела функции по ее типуf :: a -> a
Возможное решениеf a = a
f = id
id 4242
Логика типов
Какие возможны реализации?f :: a -> [a]
Выведение типов
Для функции factmodule Factwhere
fact 0 = 1fact n = n*fact (n-1)
получим типfact :: (Eq a, Num a) => a -> a
Выведение типов
Все типы функций могут быть выведены автоматически наэтапе компиляцииЯвное указание типа может помочь в отладкеСкомпилировалось – работает (joke)
Выведение типов
C++11 добавляет выведение типа переменных – autoauto value = 42;auto value = 1 + 2;auto value = f(42);
C++14 добавляет выведение типа, возвращаемого функцией –auto
auto foobar(int value){
return value + 1;}
Алгебраические типы данных (AlgebraicData Types)
Наиболее общий составной тип, представляющий собойтип-сумму из типов-произведений (wikipedia)
Тип с одним параметром
Множество AПеременная типа a
data Name a = Constructor a
Тип с несколькими параметрами
Множества T, A и BПеременные типа a и b
data Name a b = Constructor a b
тип-произведениеT = A× B
Тип с несколькими конструкторами
Множества T и AПеременная типа a
data Name a = ContructorF a | ContructorS a
тип-суммаAf = {(x , 1) : x ∈ A}
As = {(x , 2) : x ∈ A}
T = Af ∪ As
Все вместе
Множества T, A, BПеременные типа a и b
data Name a b = ContructorF a b | ContructorS a
сумма произведений
Af = {(x , 1) : x ∈ A× B}
As = {(x , 2) : x ∈ A}
T = Af ∪ As
Дизъюнктное объединение (Disjointunion)
⋃i∈I{(x , i) : x ∈ Ai}
Зачем
• Формальное обощение: enum, struct, union• Сопоставление параметров функций по шаблону
Параметры функций
data State a b = First a | Second b | Third a b
handle (First s) v = Second (f s)handle (Second s) v = First shandle (Third s t) v = Third s (f t v)
enum
data Bool = True | Falsedata Color = Red | Green | Blue
enum Bool{
TRUE ,FALSE
};
struct
data State a b = St a b
struct State{
int a;int b;
};
union
data State a b = First a b | Second a
union State{
struct{
int a;int b;
} first;struct{
int a;} second;
};
Рекурсивные типы данных
Бинарное деревоmodule Treewhere
data Tree a = Leaf | Node (Tree a) a (Tree a)
sumTree Leaf = 0sumTree (Node left v right) = sumTree left + v + sumTree right
import Tree
main =putStrLn $ show (sumTree (Node (Node (Leaf) 2 (Node (Leaf) 3 (Leaf))) 1 (
Leaf)))
6
Списки
List a = [] | (:) a (List a)
Полиморфизм
Позволяет строить суждения о типах зная только класс этихтиповКласс описывает функции, реализуемые типом
Typeclasses
Моноид - множество с заданной на нем ассоциативнойбинарной операцией и нейтральным элементом.
(a · b) · c = a · (b · c)
a · e = a
Пример: целые числа с операцией умножения.Что насчет чисел с плавающей запятой?
Typeclasses
module Monoidwhere
class Monoid a wherebinop :: a -> a -> aidentity :: a
twice :: Monoid a => a -> atwice a = binop a a
Typeclasses
“In fact, trolls traditionally count like this: one, two, three . . .many, and people assume this means they can have no grasp ofhigher numbers.” – Men at Arms, Terry Pratchett
Typeclasses
module TrollNum(TrollNum (..), plus , binop , identity)where
import Monoid
data TrollNum = Zero | One | Two | Three | Manyderiving Show
plus :: TrollNum -> TrollNum -> TrollNumplus a b = fromNum (toNum a + toNum b)
instance Monoid TrollNum wherebinop = plusidentity = Zero
toNum Zero = 0toNum One = 1toNum Two = 2toNum Three = 3toNum Many = 4
fromNum 0 = ZerofromNum 1 = OnefromNum 2 = TwofromNum 3 = ThreefromNum _ = Many
Typeclasses
import Monoidimport TrollNum
main = putStrLn $ show $ twice Two
Many
Операторы
Среднее арифметическоеmodule Opswhere
(-|-) a b = (a + b)/2
Pointfree style
Функции f (x), g(x) и z(x)z(x) = f (g(x))
z = f . g(.) :: (b -> c) -> (a -> b) -> a -> c
Pointfree style
Функция, прибавляющая 1 к переданному значениюaddOne v = v + 1addOne v = (+) v 1addOne v = (+) 1 vaddOne = (+) 1((+)1) :: Num a => a -> a
Pointfree style
Найти длину самого длинного слова в строкеmodule LongestWordwhere
longestWord = (foldr max 0) . (map length) . words
Привет map-reduce
Понятие
Это абстракция линейной цепочки связанных вычислений. Еёосновное назначение - инкапсуляция функций с побочнымэффектом от чистых функций, а точнее их выполнений отвычислений (wikipedia).
Info
class Monad m where(>>=) :: m a -> (a -> m b) -> m b(>>) :: m a -> m b -> m breturn :: a -> m afail :: String -> m a
Общий шаблон
• Тип с одним параметром m• Получение значения (bind):функция (»=) :: m a -> (a -> m b) -> m b
• Инъекция значения (inject):функция return :: a -> m a
Monad
IO
Как возможно IO в системе без побочных эффектов?IO monad
Снова факториал
Хвостовая рекурсияmodule XFactwhere
xfact n = xfact ’ 1 nwhere
xfact ’ v 0 = vxfact ’ v n = xfact ’ (v*n) (n-1)
Выведение типов
Модульная арифметикаmodule ModIntwhere
newtype ModInt = ModInt {fromModInt :: Int
}deriving (Show)
instance Num ModInt where(+) (ModInt a) (ModInt b) = ModInt ((a + b) ‘mod ‘ 23)(*) (ModInt a) (ModInt b) = ModInt ((a * b) ‘mod ‘ 23)abs (ModInt a) = ModInt (abs a)signum (ModInt a) = ModInt (signum a)fromInteger v = ModInt (fromInteger v ‘mod ‘ 23)
import ModInt
pow a 0 = 1pow a b = a * pow a (b-1)
main = putStrLn $ show $ fromModInt $ pow 2 5
9
DSL
import Fuzzimport DumbFuzzer
authRequest = "AUTH" ‘wait ‘ authResponsesgetRequest = "GET" ‘wait ‘ getResponsesauthResponses = ("AUTHED" |-> getRequest) // (other (report 0))getResponses = ("200␣OK" |-> report 1) // ("418␣I’m␣a␣teapot" |-> report 42) //
(other (report 0))
main = dor <- fuzz DumbFuzzer authRequestputStrLn $ show r
Fuzz I
module Fuzz(wait , report , (|->), (//), other , fuzz , Fuzzer (..))where
data Response r = Response (Msg , Request r) | Other (Request r)data Request r = Send Msg [Response r] | Report r
report :: r -> Request rreport r = Report r
wait :: Msg -> [Response r] -> Request rwait message reactions = Send message reactions
other :: Request r -> [Response r]other response = [Other response]
(|->) :: Msg -> Request r -> [Response r](|->) message action = [Response (message , action)]
(//) :: [Response r] -> [Response r] -> [Response r](//) a b = a ++ b
fuzz :: Fuzzer f => f -> Request r -> IO rfuzz f (Send msg responses) = do
r <- send f msgnext f r responses
fuzz f (Report r) = return r
class Fuzzer f wheresend :: f -> Msg -> IO Msg
Fuzz II
next f _ [Other request] = fuzz f requestnext f resp ((Other request):rs) = next f resp (rs ++ [Other request ])next f resp (( Response (msg , request)):rs) | resp == msg = fuzz f request
| otherwise = next f resp rs
type Msg = String
Fuzzer
module DumbFuzzerwhere
import Fuzz
data DumbFuzzer = DumbFuzzer
instance Fuzzer DumbFuzzer wheresend _ m = do
putStrLn mgetLine
Литература
• Real World Haskell by Bryan O’Sullivan, John Goerzen, andDon Stewart
• The Haskell Road to Logic, Math and Programming by KeesDoets and Jan van Eijck
• Parallel and Concurrent Programming in Haskell by SimonMarlow
• Haskell Beats C Using Generalized Stream Fusion by GeoffreyMainland, Roman Leshchinskiy, and Simon Peyton Jones(paper)