haskell
DESCRIPTION
Функциональное программирование в примерах. Язык Haskell: характеристики, история, сильные и слабые стороны, истории успеха и неудач. Спецификация Haskell’98: синтаксис, компиляторы, интепретаторы, документация, IDE. Особенности языка: тип Maybe, списки, классы типов, основы монад. Библиотеки и фреймворки: Parsec, GenXml, HaXml DSL На десерт что-то из Existential Types, State Monad, ST Monad, Monad Transformers.TRANSCRIPT
HaskellАлександр Гранин
О чем доклад?● Часть 1. Ликбез по ФП и Haskell
○ Функциональное программирование○ Язык Haskell. Применимость языка○ Язык Haskell. Основы○ Инструменты разработчика
● Часть 2. Haskell: We need to go deeper○ Обработка данных: NgnTrafficParser○ XML и Domain Specific Languages○ Parsec, HaXml, GenXml
● Часть 3. Немного хардкора○ Монады!!!
Программирование:
● Императивное
● Объектно-ориентированное
● Функциональное
● Логическое
● Декларативное
(println "Hello World!")
printfn "Hello World!"
io:format("Hello, World!~n").
putStrLn "Hello World!"
println("Hello World!")
Функциональное программирование
?
Функциональное программирование
● Все есть функция
● ...
● ...
● ...
● ...
● ...
Функциональное программирование
● Все есть функция
● Рекурсия
● ...
● ...
● ...
● ...
Функциональное программирование
● Все есть функция
● Рекурсия
● Функции высших порядков
● ...
● ...
● ...
Функциональное программирование
a := 1a := 2
DO NOT CHANGE!The immutability is with you
● Все есть функция
● Рекурсия
● Функции высших порядков
● Иммутабельность данных
● ...
● ...
Функциональное программирование
● Все есть функция
● Рекурсия
● Функции высших порядков
● Иммутабельность данных
● Чистые функции
● ...
Функциональное программирование
● Все есть функция
● Рекурсия
● Функции высших порядков
● Иммутабельность данных
● Чистые функции
● Нет побочных эффектов
Язык Haskell
?
Язык Haskell
Haskell - это...
● Чистый функциональный,● строго статически типизированный,● кроссплатформенный,● компилируемый язык● общего назначения● с ленивой семантикой● и автоматическим выводом типов.
Краткая история Haskell
● В честь Хаскеля Карри
● GHC: Саймон Пейтон Джонс
● Семейство языков ML
● Прародитель - Miranda
● 1990 год - Haskell 1.0
● 1998 год - Haskell '98
● 2009 год - Haskell 2010
«Доказательство — это программа, а
доказываемая формула — это тип
программы»
(c) M
iran
Lipo
vača
Где Haskell плох:
● Системное программирование
● Real-time системы
● GUI
Где Haskell хорош:
● Надежность кода
● Надежность кода
● Domain Specific Languages
Abstract Syntax Tree
Code base
Где Haskell хорош:
● Надежность кода
● Domain Specific Languages
● Безопасный параллелизм
Где Haskell хорош:
● Надежность кода
● Domain Specific Languages
● Безопасный параллелизм
● Обработка данных
Телекомы: TrafficParser
Где Haskell хорош:
● Надежность кода
● Domain Specific Languages
● Безопасный параллелизм
● Обработка данных
● Парсинг
Где Haskell хорош:
Name
Description
Body Type
XML
DSL
Haskell - не мэйнстрим?..
Причины непопулярности
● Pascal, C, C++, Java, C# - в вузах● Традиционное мировоззрение очень сильно● Заработать на Haskell очень трудно● Мифы и стереотипы
"Избегать успеха любой ценой."Саймон Пейтон Джонс
Язык Haskell: Основы
fib n = case n of 0 -> 0 1 -> 1 n -> fib (n-1) + fib (n-2)
fact n = case n of 0 -> 1 n -> fact (n-1) * n
Функции.case - аналог switch
Функции, аргументы - lowerCamelCase
fib 0 = 0fib 1 = 1fib n = fib (n-1) + fib (n-2)
fact 0 = 1fact n = fact (n-1) * n
Сопоставление с образцом
Функции, аргументы - lowerCamelCase
fib :: Word -> Wordfib 0 = 0fib 1 = 1fib n = fib (n-1) + fib (n-2)
fact :: Word -> Wordfact 0 = 1fact n = fact (n-1) * n
Система типовФункции, аргументы - lowerCamelCase
Типы, классы типов, модули - UpperCamelCase
fact :: Word -> Wordreplicate :: Int -> a -> [a]map :: (a -> b) -> [a] -> [b](+) :: Int -> Int -> Intadd :: Int -> Int -> Int
Система типов
Списки. Кортежи
phonebook :: [(String, String)]phonebook = [ ("Bob", "953 777-44-45") , ("Fred", "919 33-555-11") , ("Alice", "383 11111111") , ("Jane", "964 4000004") ]
Списки. Кортежиtype PhoneBook = [(String, String)]
validate :: PhoneBook -> PhoneBookvalidate book= map addPrefix book
addPrefix :: (String, String) -> (String, String)addPrefix (name, phone) = (name, "+7 " ++ phone)
Лямбды - анонимные функции
nums = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0]
oddNumbers = filter (\n -> odd n) numsoddNumbers2 = filter odd nums
// C#:int[] nums = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };var oddNumbers = nums.Where(n => n % 2 == 1);
Генераторы списков
(c) Miran Lipovača http://learnyouahaskell.com/
Cоздать список:
1. На множестве от 1 до 100.
2. Только нечетные.
3. Делящиеся на 3 без остатка.
4. Делящиеся на 7 без остатка.
list1 = [21, 63]list2 = [x | x <- [1..100], odd x, x `mod` 3 == 0, x `mod` 7 == 0]
1. x - от 1 до 100, y - от 100 до 200.
2. x - четные, y - нечетные.
3. x + y < 200.4. |x - y| > 190.
Cоздать список [(x, y)]:
list = [(x, y) | x <- [1..100], even x, y <- [100..200], odd y, x + y < 200, abs (x - y) > 190]
Алгебраические типы данных
data Bool = True | False
Тип Конструкторы
Алгебраические типы данных
data STree = Tip | Branch
{ leftBranch :: STree,value :: Int,rightBranch :: STree
}
Сопоставление с образцом
height :: STree -> Intheight Tip = 0height (Branch lt _ rt) = 1 + max (height lt) (height rt)
5
3 7
1 4
Data.Maybe - аналог Nullable
phonebook :: [(String, String)]...
printPhone :: String -> [(String, String)] -> IO ()printPhone name book = case lookup name book of Nothing -> putStrLn "Name not exist." Just phone -> putStrLn phone
data Maybe a = Nothing | Just a
lookup :: Eq a => a -> [(a, b)] -> Maybe b
Инструменты разработки
Haskell Platform
● Компилятор GHC
● Интерпретатор GHCi
● Базовые библиотеки
● Пакетный менеджер Cabal
● Документирование - Haddock
● MinGW (Windows) http://www.haskell.org/platform/
Glasgow Haskell Compiler
Компиляция:ghc --make FizzBuzz.hsghc --make -O2 FizzBuzz.hsghc --make -O2 -threaded FizzBuzz.hs
Выполнение:runghc FizzBuzz.hs
REPL - GHCi(GHC interpreter)
Read
EvalPrintLoop
REPL - GHCi(GHC interpreter)
> [1..10][1, 2, 3, 4, 5, 6, 7, 8, 9, 10]> (3 + 30)33> fizzBuzz
<interactive>:1:1:No instance for (Show (Int -> String))
arising from a use of 'print'Possible fix:
add an instance declaration for (Show (Int -> String))In a stmt of an interactive GHCi command: print it
Hackage - репозиторий библиотек
Библиотеки и программы
● xmonad - тайловый оконный менеджер● Parsec - комбинаторные парсеры● HaXmL, HXT - обработка XML● darcs - система контроля версий● Yesod - RESTful веб-фреймворк● QuickCheck - тестирование кода● House, Kinetic - операционные системы● Всевозможные эффективные коллекции● OpenGL, OpenAL, OpenCL биндинги● ... несколько игр и многое другое
IDE
EclipseFP
Leksah
SublimeHaskell
Want more?
NGN Traffic Parser
|R200|99999|333333|CR,CS,AM|1|1|3022|222222|333333|||...|R200|44455|012345|CR,CS,AM|1|1|3022|555555|111111|||...|R200|45678|543210|CR,CS,AM|1|1|3022|444444|555555|||...|R200|88888|222222|CR,CS,AM|1|1|3022|666666|777777|||...
file1.txt file2.txt .........
Billing
CMD, DTS, SQL
NGN Traffic Parser
Billing
Merge Files
Process data
MS SQL DTS
АТД: Predicatedata Predicate = NotInList [ByteString] | InList [ByteString] | Like [ByteString] | LengthLess Int
type PredicateMap = [(FieldIndex, Predicate)]
predicates = [ (7, NotInList (map C.pack ["3022", "3012"])) , (8, Like [C.pack "44", C.pack "45"]) , (9, LengthLess 7) ]
NGN Traffic Parser
checkPredicate :: Predicate -> C.ByteString -> BoolcheckPredicate (NotInList l) str = (not . elem str) lcheckPredicate (InList l) str = elem str lcheckPredicate (LengthLess n) str = (length str) < n
Алгебраический тип данных,Сопоставление с образцом
replaceSymbols :: String -> StringreplaceSymbols s = map (replaceChar '|' ' ') ((map (replaceChar ' ' '*') (refieldDoubles s)))
Ver. 2.0ByteString
Ver. 1.0String
XML и Domain Specific Languages
<?xml version="1.0" encoding="utf-8" ?>- <PolicySet xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" PolicySetId="PolicSet-04b0613533354df196715b032388325c" Version="1.0" PolicyCombiningAlgId="urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:permit-overrides"> <Description>All active policices</Description> <Target />- <Policy PolicyId="a25099f1-d6fd-412e-b827-65fd39f799e1" Version="2.1.1 <Description>Match file(s) content</Description> <Target />
Политики безопасности
XML
Тестирование политики
Алгоритмы тестирования:Безопасность обеспечена?
Нет
Исправляем дыры
Да
ОК, запускаем!!
Policy.xml
Policy.xml
XML?!. Сложно! Хочу DSL!
Алгоритмы тестирования:Безопасность обеспечена?
Нет
Исправляем дыры
Да
ОК, запускаем!!
MyPolicy.dsl
Пример DSL
efm := ExactFileMatching 2 ".\test2.db" 0cat2 : "CAT2" = Category [] NoMatchcat1 := Category [cat2] NoMatchcat3 := Category [] NoMatchcat0 := TopCategory [cat1, cat3] NoMatchrule := Rule Permit [] | n_of 1 (Evaluate [efm]) [cat0]policy := Policy DenyOverrides [Audit Deny Medium] [rule]policySet := PolicySet PermitOverrides [] [policy]
Архитектура DSL <-> XML
Policy.xml
Policy.dsl
Policy AST
HaXmlGenXml
Custom writerParsec
DSL AST
Translator
DSL: AST на ADTdata DslTokenName = DslTokenName String
data DslTokenBody = DslGuardedBody { dtbDslAdt :: DslAdt , dtbDslExpression :: DslExpression } | DslNonGuardedBody { dtbDslAdt :: DslAdt } data DslToken = EmptyDslToken | DslToken { dtDslTokenType :: DslTokenType , dtDslTokenName :: DslTokenName , dtDslTokenDescr :: DslTokenDescription , dtDslTokenBody :: DslTokenBody }
Parsec: парсинг DSLdslToken :: GenParser Char st DslTokendslToken = do tName <- dslTokenName tDescr <- dslTokenDescription (tBody, tType) <- dslTokenBody return (DslToken tType tName tDescr tBody)
dslTokenBody :: GenParser Char st (DslTokenBody, DslTokenType)dslTokenBody = spaces >> ( try dslGuardedTokenBody <|> try dslNonGuardedTokenBody <?> "dslTokenBody")
NonGuardedBody
GuardedBody
Name
Description
Body Type
DslToken
DslTokenBody
Связь с БНФ
lower ::= 'a' | 'b' | ... | 'z'rest ::= (letters | numbers | '_') + rest | ''identifier ::= (lower | '_') + resttokenName ::= identifiertokenBody ::= guardedBody | nonGuardedBodytoken ::= (tokenType, tokenName, tokenDescr, tokenBody)
identifier :: GenParser Char st Stringidentifier = do c <- (lower <|> char '_') rest <- many (alphaNum <|> char '_') return (c : rest)
HaXml: парсинг XMLtoCondition :: Content i -> Maybe ConditiontoCondition e = let exprCond = head . filterNonTextContent $ children e expr = fromJust $ recognizeStructure expRecognizers
exprCondin Just (Condition expr)
-- ............ Здесь много кода
parsePolicy :: String -> PolicySetparsePolicy content = let (Document _ _ root _) = xmlParse "error.log" content in toPolicySet (CElem root noPos)
GenXml: генерация XMLwriteDteValue :: DataTypeExt -> Xml ElemwriteDteValue (DteString s) = xtext swriteDteValue (DteBoolean b) = xtext (map toLower (show b))writeDteValue (DteInteger i) = xtext (show i)
writeAttributeValue :: AttributeValue -> Xml ElemwriteAttributeValue (AttributeValue dataType val) = let attrValAttributes = xattr dataTypeN dataType attrValVals = writeDteValue val in xelem attributeValueN (attrValAttributes <#> attrValVals)
А теперь - хардкор!!!
lookup :: Eq a => a -> [(a, b)] -> Maybe b
class Eq a where (==), (/=) :: a -> a -> Bool
instance Eq Char where c1 == c2 = ... -- compare chars
c1 /= c2 = not (c1 == c2)
instance Eq Int where i1 == i2 = ... -- compare ints i1 /= i2 = not (i1 == i2)
Классы типов -"интерфейсы" на стероидах
data Predicate = NotInList [ByteString] | InList [ByteString] | Like [ByteString] | LengthLess Int deriving (Eq, Show, Read)
Классы типов -"интерфейсы" на стероидах
Монада IO:Безопасный ввод-выод
let rndGen1 = mkStdGen 100
let (val1, rndGen2) = random rndGen1
let (val2, rndGen3) = random rndGen2
Отправной пример:Random generator
Стратегия связывания, IO
getName :: IO ()getName = do putStrLn "What is your name?" yourName <- getLine putStr "Hello, " putStrLn (yourName ++ "!")
let world0 = getWorld
let (val1, world1) = ioAction1 world0
let (val2, world2) = ioAction2 world1
do-нотация
getName :: IO ()getName = do putStrLn "What is your name?" yourName <- getLine putStr "Hello, " putStrLn (yourName ++ "!")
getName' = putStrLn "What is your name?" >> getLine >>= \yourName -> putStr "Hello, " >> putStrLn (yourName ++ "!")
Возвращаемое значение
getName :: IO StringgetName = do putStrLn "What is your name?" yourName <- getLine putStr "Hello, " putStrLn (yourName ++ "!") return yourName
return :: a -> m aa :: Stringm :: IO
Монада State
newtype State s a = State { runState :: s -> (a, s) }
Монада State
myFunc :: State Int IntmyFunc = do val <- get put (val - 8) get
getNumber = evalState myFunc 50main = print getNumber
Стратегия связывания, State
let state1 = evalState 50
let (val1, state2) = get state1
let ((), state3) = put (val1 - 8) state2
let (val2, state4) = get state3
Monad transformers?Зачем это надо?
Монада 2Монада 1
Monad transformers?Зачем это надо?
Монада 2Монада 1
State + IO
data GS = GS { worldMap :: [Location] , currentLocation :: Location , welded :: Bool , bucketFull :: Bool } deriving (Show)
newtype GameState a = GameState { runGameState :: StateT GS IO a }
State + IO
run :: GameState Resultrun = do t <- get -- read a command from the user io . putStr $ "> " io . hFlush $ stdout line <- io getLine result <- case parseCommand line of Nothing -> write "Invalid command!" >> continue Just cmd -> do
...........
Named + IO
-- Int-named IO calculations:intNamedFunc :: NamedT Int IO IntintNamedFunc = do name <- getName return name
testIntNamed :: IO ()testIntNamed = do name <- evalNamedT intNamedFunc 1 print name -- You got output: 1
HaskellScala
Clojure...
Want more?
PLUGIN!!!
FUNction
It's all Haskell is about
Lambda The Gathering
Lambda Lifter
http://bit.ly/17CyYFj
Haskell Links
http://hackage.haskell.org/packages/hackage.html
Hackage - репозиторий библиотек
Haskell Platform
● Компилятор GHC
● Интерпретатор GHCi
● Базовые библиотеки
● Пакетный менеджер Cabal
● Документирование - Haddock
● MinGW (Windows) http://www.haskell.org/platform/
http://habrahabr.ru/company/selectel/blog/13585
8/
Success Stories
Success Stories
Want more?
http://www.haskell.org/haskellwiki/Haskell_in_industry
Класс типов Monad
class Monad m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a
Монада Named -именованные вычисления
data Named n a = Named { runNamed :: (n -> a) }
instance Monad (Named n) where return x = Named (\_ -> x)
x >>= f = Named (\name -> let a = runNamed x name in runNamed (f a) name)