Защо трябва да обичаме Haskell?

PlovdivConf 2014

Аз съм РадоРадо

Тъй като може да не ми стигне времето, ще се представя накрая.

Тази презентация има три основни цели

  1. Да се замислите върху това как пишете код.
  2. След като се приберете у вас, да разцъкате Haskell.
  3. Да станете по-добри програмисти.
  4. (Няма да ви карам да си сменяте езика на Haskell.)

Бърза Agenda

1. Малко мотивация за Haskell.

2. Haskell crash course.

3. Ще направим едно просто приложение с IO.

Все повече езици се движат към "функционалната" парадигма.

Haskell сe води неизползваем език.

В Haskell има толкова много неща за учене, че граничи с неизползваемост, но ...

Има и много добри идеи, с които човек си заслужава да се запознае.

Писането на Haskell те кара да мислиш.

Както за решаването на проблема, така и за дизайна на кода.

Какво представлява Haskell?

module Split ( split ) where

split :: Eq a => [a] -> [a] -> [[a]]
split delimiter items = go delimiter items [[]]
  where
    dl = length delimiter
    go _ [] result = result
    go xs (y:ys) result@(r:rs)
      | xs == (take dl (y:ys)) = go xs (drop dl (y:ys)) ([] : result)
      | otherwise = go xs ys ((y:r):rs)

Език без променливи.

a = a + 1 не съществува

Език без структури за цикъл.

Рекурсията е вашият най-добър приятел.

Pure Functional Language.

No Side Effects.

Side effect = changing something somewhere.

  • Променяне на стойност на променлива.
  • Писане в база от данни.
  • Промяна на състоянието в някакъв UI.

Викаме два пъти една и съща функция. Получаваме различен резултат.

# Python
x = 2;

def inc_x():
    global x
    x = x + 1


def times_x(n):
    global x
    return x * n

times_x(5)
inc_x()
times_x(5)

Pure Functions.

-- a.hs (this is a comment)
f x = x * x
  • Без глобален state.
  • Без странични ефекти.
  • Винаги връщат стойност.

Статично типизиран език.

-- b.hs
inInterval :: Int -> Int -> Int -> Bool
inInterval a b x = x >= a && x <= b
                    

Type Inference.

-- c.hs
confIt cities = map (\city -> city ++ "Conf") cities

*Main> confIt ["Plovdiv", "Varna", "Tarnovo", "Pleven"]
["PlovdivConf","VarnaConf","TarnovoConf","PlevenConf"]
*Main> :t confIt
confIt :: [[Char]] -> [[Char]]

Компилаторът е по-умен от нас.

confIt :: [[Char]] -> [[Char]]

String = [Char]

Types.

Стандарните неща, които очаквате от един типизиран език.

  • Int
  • Integer
  • Bool
  • String
  • Double
  • etc.

Нека имаме функцията first, която връща първи елемент на списък.

-- d.hs
first [] = error "Empty list"
first (x:_) = x

-- first [1,2,3] -> 1
-- first ["asd", "wtf"] -> "asd"
-- first [True, False] -> True

Според вас, каква е сигнатурата на first?

Polymorphic functions


a може да е всякакъв тип.

-- d.hs
first :: [a] -> a
first [] = error "Empty list"
first (x:_) = x

Typeclasses.

Малко по-дълбоко.

member [] = False
member y (x:xs) = y == x || member y xs

*Main> :t member
member :: Eq a => a -> [a] -> Bool

Typeclasses ~~ Interfaces

  • Типовете сами казват към кои типови класовe принадлежат.

  • Това ги задължава да имплементират определени функции.

  • Eq е типов клас, който иска имплементация на функцията (==)

class Eq a where
    (==) :: a -> a -> Bool
    (/=) :: a -> a -> Bool
    x == y = not (x /= y)
    x /= y = not (x == y)

Eq в Haskell изглежда така:

Добавяме наш тип в Eq:

data TrafficLight = Red | Yellow | Green
                    
instance Eq TrafficLight where
    Red == Red = True
    Green == Green = True
    Yellow == Yellow = True
    _ == _ = False

Referential Transperency

Една функция, извикана два пъти с еднакви аргументи, трябва да даде един и същи резултат.


# Python
from random import randint
randint(1, 10) # 5
randint(1, 10) # 6

Как правим random в Haskell?

Как правим IO в Haskell?

Как пазим състояние в Haskell?

return 4;

Счупихме Haskell ;(

-- f.hs
import System.Random

randomRange a b = getStdRandom (randomR (a,b))

Сигнатурата на randomRange?

-- f.hs
import System.Random

randomRange :: Random a => a -> a -> IO a
randomRange a b = getStdRandom (randomR (a,b))

IO a

Не може да направим следното нещо:

f :: IO String -> String

Pure vs. Impure functions

IO е като зомби зараза.

Каквото се докосне до IO, става IO

Hello World!

-- g.hs
main :: IO ()
main = putStrLn "Hello World!"

Read & Print

=<<, >>=, <-, ->, <*>, <$>, asd, wtf, etc.

-- h.hs
main :: IO ()
main = getLine >>= putStrLn

Do Syntax

-- i.hs
import System.Random

randomRange :: Random a => a -> a -> IO a
randomRange a b = getStdRandom (randomR (a,b))

main :: IO ()
main = do
    name <- getLine
    putStrLn $ "Hello, " ++ name
    randomNumber <- randomRange 1 10 :: (IO Int)
    let squareRandom = randomNumber * randomNumber
    putStrLn $ "Your lucky number is : " ++ (show randomNumber)
    putStrLn $ "And the square is : " ++ (show squareRandom)

Do you even math?

Книжка за четене*

* не се подвеждайте от цветното.

И все пак да се представя: