Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Аппликативное программирование или как парсить SQL на чистом Haskell

Аппликативное программирование или как парсить SQL на чистом Haskell

Функциональное программирование использует подходы, не похожие на те, что мы применяем каждый день: функции чистые, переменных нет, а во многих языках нет даже циклов в привычном нам смысле. Из–за этого возникают специфические проблемы, которые приходится решать, и часто эти решения реализованы довольно интересно. Однако в докладе речь пойдет о другом: мы посмотрим на то, как применение сильных сторон ФП поможет нам построить очень удобную в применении штуку. Мы соберем из подручных средств очень простой, но мощный парсер.

Первая часть доклада — про основы синтаксиса Haskell. Во второй части — поговорим про функторы, из которых мы будем собирать нашу программу. В финале — мы сделаем парсер общего назначения в аппликативном стиле и научим его разбирать небольшое подмножество языка SQL.

Dmitry Tsepelev

July 16, 2022
Tweet

More Decks by Dmitry Tsepelev

Other Decks in Programming

Transcript

  1. Аппликативное
    программирование
    или Как парсить SQL на чистом Haskell
    Дмитрий Цепелев

    View Slide

  2. DmitryTsepelev
    🌆 2
    @dmitrytsepelev
    🌎 dmitrytsepelev.dev

    View Slide

  3. DmitryTsepelev
    🌆
    Зачем это все?
    • ФП — это весело;


    • многие штуки доезжают из ФП в модные языки через 10-30 лет, можно
    заглянуть в будущее 🔮;


    • есть свои сложности и интересные способы их решать (спросите меня
    про линзы!);


    • некоторые вещи значительно проще, чем в ООП;


    • сам Haskell простой как 🚪, сложные концепции надстраиваются (одну
    штуку мы увидим прямо сейчас!).
    3

    View Slide

  4. Haskell 101

    View Slide

  5. DmitryTsepelev
    🌆
    Типы
    5
    Int
    - -
    целое число


    String
    - -
    строка


    type IntList = [Int]
    - -
    список


    type String = [Char]
    - -
    строки это списки букв


    (Int, Int)
    - -
    пара

    View Slide

  6. DmitryTsepelev
    🌆
    Функции
    • все функции чистые;


    • состояние не мутируется;


    • функция принимает 0 или более
    аргументов и возвращает одно
    значение;


    • могут вызываться в инфиксной форме с
    помощью ``.
    6
    sum
    : :
    Int
    - >
    Int
    - >
    Int


    sum x y = x + y


    sum 3 6
    - -
    9


    3 `sum` 6
    - -
    9

    View Slide

  7. DmitryTsepelev
    🌆
    Функции: инфиксная форма
    • in
    fi
    x делает функцию инфиксной по умолчанию;


    • первый аргумент — приоритет функции;


    • могут вызываться в инфиксной форме с
    помощью ().
    7
    sum
    : :
    Int
    - >
    Int
    - >
    Int


    sum x y = x + y


    inf
    i
    x 6 /\/\


    (/\/\) = sum


    1 /\/\ 2
    - -
    3


    (/\/\) 1 2
    - -
    3

    View Slide

  8. DmitryTsepelev
    🌆
    Изменение приоритета операций: $
    • выполняется сначала правая часть, затем левая;


    • позволяет избавиться от лишних скобок;


    • не является частью синтаксиса языка.
    8
    show 1 + 1
    - -
    No instance for (Num String) arising from a use of +


    show (1 + 1)
    - -
    2


    inf
    i
    xr 0 $


    ($)
    : :
    (a
    - >
    b)
    - >
    a
    - >
    b


    f $ x = f x


    show $ 1 + 1
    - -
    2

    View Slide

  9. DmitryTsepelev
    🌆
    Частичное применение функции
    • можно считать, что функция всегда
    принимает и возвращает один аргумент;


    • если аргументов больше — вернется
    новая функция;


    • такая операция называется
    каррированием.
    9
    sum
    : :
    Int
    - >
    (Int
    - >
    Int)


    sum x y = x + y


    sum3
    : :
    Int
    - >
    Int


    sum3 = sum 3


    sum3 6
    - -
    9

    View Slide

  10. DmitryTsepelev
    🌆
    Композиция
    • функция . (точка) позволяет из двух функций создать одну, вызывающую
    обе по очереди;


    • можно убрать немного скобок.
    10
    inf
    i
    xr 9 .


    (.)
    : :
    (b
    - >
    c)
    - >
    (a
    - >
    b)
    - >
    a
    - >
    c


    (.) f g a = f (g a)


    succ . (*2) $ 3
    - -
    7

    View Slide

  11. DmitryTsepelev
    🌆
    Алгебраические типы
    умножение (product/И): тип,
    состоящий из нескольких других типов
    11
    data User = User String String


    User "[email protected]" "Jonh Doe"
    сложение (sum/ИЛИ): тип,
    который может принимать
    значения разных типов
    data UserType = Admin | User | Guest

    View Slide

  12. DmitryTsepelev
    🌆
    Алиасы типов
    • для читабельности можно создать алиасы (Email, Name);


    • можно явно указать названия в сигнатуре чтобы сгенерировать
    функции.
    12
    type Email = String


    type Name = String


    data User = User Email Name


    User "[email protected]" "Jonh Doe"


    data User2 = User { email
    : :
    String, name
    : :
    String }

    View Slide

  13. DmitryTsepelev
    🌆
    Параметризованные типы
    • параметризованный тип может содержать значение произвольного
    типа;


    • на тип можно накладывать ограничения;


    • Just в примере ниже — конструктор.
    13
    data Maybe a = Just a | Nothing


    Just 3

    View Slide

  14. DmitryTsepelev
    🌆
    Pattern matching и guard conditions
    • можно проверять тип значения и разбирать его в теле функции;


    • можно использовать guard conditions чтобы проверять более сложные
    условия.
    14
    sayHi
    : :
    Maybe String
    - >
    String


    sayHi (Just name) = "Hi, "
    + +
    name
    + +
    "!"


    sayHi Nothing = "Hi!"


    sayHi mName | isJust mName = "Hi, "
    + +
    (fromJust name)
    + +
    "!"


    | otherwise = "Hi!"


    View Slide

  15. DmitryTsepelev
    🌆
    Классы типов
    • аналог интерфейсов в ООП;


    • содержит функции, которые
    должен реализовывать тип,
    который реализует данный класс
    типов;


    • может содержать реализации по
    умолчанию.
    15
    class Num a where


    (+), (-), (*)
    : :
    a
    - >
    a
    - >
    a


    negate
    : :
    a
    - >
    a


    abs
    : :
    a
    - >
    a


    signum
    : :
    a
    - >
    a


    fromInteger
    : :
    Integer
    - >
    a


    x - y = x + negate y


    negate x = 0 - x

    View Slide

  16. DmitryTsepelev
    🌆
    Реализация классов типов
    • нужно объявить все
    обязательные методы;


    • если класс типов содержит
    минимальную реализацию, то
    его можно объявить в
    сокращенной форме.
    16
    instance Num Integer where


    (+) = integerAdd


    (-) = integerSub


    (*) = integerMul


    negate = integerNegate


    fromInteger i = i


    abs = integerAbs


    signum = integerSignum


    data User = User { email
    : :
    String } deriving (Show)


    User "[email protected]"


    - -
    User {email = "[email protected]"}

    View Slide

  17. DmitryTsepelev
    🌆
    Более гибкие функции
    • вместо конкретных типов можно использовать переменные–типы (type
    variables);


    • при вызове будут использованы конкретные типы;


    • для того, чтобы ограничить подходящие типы, можно использовать
    классы типов (перед
    = >
    ).
    17
    sum
    : :
    Num a
    = >
    a
    - >
    a
    - >
    a


    sum x y = x + y


    sum 3 6
    - -
    9

    View Slide

  18. Функторы и аппликативные
    функторы

    View Slide

  19. DmitryTsepelev
    🌆
    Работа со значением в коробке
    19
    type UserID = Int


    type Email = String


    data Maybe a = Just a | Nothing deriving (Show)


    getEmail
    : :
    UserID
    - >
    Maybe Email


    getEmail 42 = Just "[email protected]"


    getEmail _ = Nothing


    formatEmail
    : :
    Maybe Email
    - >
    Maybe Email


    formatEmail (Just email) = Just $ "Email: "
    + +
    email


    formatEmail Nothing = Nothing


    formatEmail $ getEmail 42
    - -
    Just "Email: [email protected]"

    View Slide

  20. DmitryTsepelev
    🌆
    Класс типов Functor
    20
    class Functor f where


    fmap
    : :
    (a
    - >
    b)
    - >
    f a
    - >
    f b
    • преобразовывает значение в коробке с учетом типа коробки.

    View Slide

  21. DmitryTsepelev
    🌆
    Реализация Functor для Maybe
    21
    data Maybe a = Just a | Nothing deriving (Show)


    instance Functor Maybe where


    fmap f (Just x) = Just (f x)


    fmap f Nothing = Nothing


    type UserID = Int


    type Email = String


    getEmail
    : :
    UserID
    - >
    Maybe Email


    getEmail 42 = Just "[email protected]"


    getEmail _ = Nothing


    formatEmail
    : :
    Maybe Email
    - >
    Maybe Email


    formatEmail = fmap (\email
    - >
    "Email: "
    + +
    email)
    • если значение Just — происходит
    распаковка, применение функции
    и запаковка;


    • Nothing остается без изменений.

    View Slide

  22. DmitryTsepelev
    🌆
    <$> — инфиксный fmap
    22
    data Maybe a = Just a | Nothing deriving (Show)


    instance Functor Maybe where


    fmap f (Just x) = Just (f x)


    fmap f Nothing = Nothing


    inf
    i
    x 4
    < $ >

    (
    < $ >
    ) = fmap


    type UserID = Int


    type Email = String


    getEmail
    : :
    UserID
    - >
    Maybe Email


    getEmail 42 = Just "[email protected]"


    getEmail _ = Nothing


    formatEmail
    : :
    Maybe Email
    - >
    Maybe Email


    formatEmail maybeEmail = ("Email: "
    + +
    )
    < $ >
    maybeEmail

    View Slide

  23. DmitryTsepelev
    🌆
    Реализация Functor для пары
    23
    • у пары есть семантика;


    • функция применяется ко второму элементу.
    instance Functor ((,) a) where


    fmap f (x,y) = (x, f y)

    View Slide

  24. DmitryTsepelev
    🌆
    Реализация Functor для списка
    24

    View Slide

  25. DmitryTsepelev
    🌆
    Аппликативные функторы
    • fmap принимает функцию с одним аргументом;


    • у нас есть каррирование, поэтому все функции подходят;


    • что будет, если передать функцию с двумя аргументами?
    25
    (+1)
    < $ >
    Just 42
    - -
    Just 43


    (+)
    < $ >
    Just 42
    - -
    Just (42 +)

    View Slide

  26. DmitryTsepelev
    🌆
    Класс типов Applicative
    • теперь мы можем упороться и применить функцию в коробке к
    значению в коробке;


    • pure заворачивает значение в минимально простой контейнер;



    < * >
    достает значение из коробки слева и применяет к значению в
    коробке справа, затем кладет всё в исходную коробку (если сможет!).
    26
    class (Functor f)
    = >
    Applicative f where


    pure
    : :
    a
    - >
    f a


    (
    < * >
    )
    : :
    f (a
    - >
    b)
    - >
    f a
    - >
    f b

    View Slide

  27. DmitryTsepelev
    🌆
    Реализация Applicative Functor для Maybe
    27
    data Maybe a = Just a | Nothing deriving (Show)


    instance Functor Maybe where


    fmap f (Just x) = Just (f x)


    fmap f Nothing = Nothing


    inf
    i
    xl 4
    < $ >

    (
    < $ >
    ) = fmap


    instance Applicative Maybe where


    pure = Just


    Nothing
    < * >
    _ = Nothing


    (Just f)
    < * >
    something = f
    < $ >
    something


    formatEmail
    : :
    Maybe Email
    - >
    Maybe Email


    formatEmail maybeEmail = pure (
    + +
    )
    < * >
    maybeEmail
    < * >
    Just "Email: "
    • pure — кладет значение в
    Just;


    • <*> вернет Nothing если
    Nothing будет слева либо
    справа;


    • иначе — обычный fmap.

    View Slide

  28. DmitryTsepelev
    🌆
    Каррирование в аппликативном стиле
    • можно создавать функции, работающие только с «хорошим
    сценарием»;


    • плохой сценарий будет обработан реализацией аппликативного
    функтора для используемого типа.
    28
    sum3
    : :
    Num n
    = >
    n
    - >
    n
    - >
    n
    - >
    n


    sum3 x y z = x + y + z


    pure sum3
    < * >
    Just 3
    < * >
    Just 2
    < * >
    Just 4
    - -
    Just 9


    pure sum3
    < * >
    Just 3
    < * >
    Nothing
    < * >
    Just 4
    - -
    Nothing

    View Slide

  29. DmitryTsepelev
    🌆
    *> <*
    • класс типов Applicative содержит
    дополнительные операции, которые
    могут быть выведены из <*>;


    • *> выполняет операцию, но игнорирует
    значение левого аргумента;


    • <* выполняет операцию, но игнорирует
    значение правого аргумента.
    29
    pure (42+)
    * >
    Just 2
    - -
    Just 2


    pure (42+)
    * >
    Nothing
    - -
    Nothing


    Nothing
    * >
    Just 2
    - -
    Nothing


    pure (42+)
    < *
    Just 2
    - -
    Just (42+)


    Nothing
    < *
    Just 2
    - -
    Nothing


    pure (42+)
    < *
    Nothing
    - -
    Just (42+)

    View Slide

  30. Аппликативный парсер за 15
    минут

    View Slide

  31. DmitryTsepelev
    🌆
    Простейший парсер
    31
    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    satisfy
    : :
    (Char
    - >
    Bool)
    - >
    Parser Char


    satisfy pr = Parser f where


    f "" = Left "unexpected end of input"


    f (c:cs) = if pr c then Right (cs, c) else Left ("unexpected "
    + +
    [c])


    anyChar
    : :
    Parser Char


    anyChar = satisfy (const True)


    parse anyChar "ABC"
    - -
    Right ("BC",'A')

    View Slide

  32. DmitryTsepelev
    🌆
    Простейший парсер
    32
    import Data.List (isPref
    i
    xOf)


    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    char
    : :
    Char
    - >
    Parser Char


    char p = satisfy (
    = =
    p)


    parse (char 'A') "ABC"
    - -
    Right ("BC",'A')


    parse (char 'B') "ABC"
    - -
    Left "unexpected A”


    string
    : :
    String
    - >
    Parser String


    string str = Parser f where


    f s | str `isPref
    i
    xOf` s = Right (drop (length str) s, str)


    | otherwise = Left $ "unexpected "
    + +
    s
    + +
    ", expected "
    + +
    str


    parse (string "AB") "ABC"
    - -
    Right ("C","AB")

    View Slide

  33. DmitryTsepelev
    🌆
    Реализуем функтор
    33
    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    instance Functor Parser where


    fmap f (Parser p) = Parser $ fmap (fmap f) . p


    parse (toLower
    < $ >
    char 'A') "ABC"
    - -
    Right ("BC",'a')

    View Slide

  34. DmitryTsepelev
    🌆
    Реализуем аппликацию
    34
    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    instance Applicative Parser where


    pure a = Parser $ \s
    - >
    Right (s, a)


    pf
    < * >
    pv = Parser $ \s
    - >

    case parse pf s of


    Right (s', g)
    - >

    case parse pv s' of


    Right (s'', a)
    - >
    Right (s'', g a)


    Left e
    - >
    Left e


    Left e
    - >
    Left e

    View Slide

  35. DmitryTsepelev
    🌆
    Реализуем аппликацию
    35
    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    instance Applicative Parser where


    pure a = Parser $ \s
    - >
    Right (s, a)


    pf
    < * >
    pv = Parser $ \s
    - >

    case parse pf s of


    Right (s', g)
    - >

    case parse pv s' of


    Right (s'', a)
    - >
    Right (s'', g a)


    Left e
    - >
    Left e


    Left e
    - >
    Left e

    View Slide

  36. DmitryTsepelev
    🌆
    Реализуем аппликацию
    36
    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    instance Applicative Parser where


    pure a = Parser $ \s
    - >
    Right (s, a)


    pf
    < * >
    pv = Parser $ \s
    - >

    case parse pf s of


    Right (s', g)
    - >

    case parse pv s' of


    Right (s'', a)
    - >
    Right (s'', g a)


    Left e
    - >
    Left e


    Left e
    - >
    Left e

    View Slide

  37. DmitryTsepelev
    🌆
    Реализуем аппликацию
    37
    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    instance Applicative Parser where


    pure a = Parser $ \s
    - >
    Right (s, a)


    pf
    < * >
    pv = Parser $ \s
    - >

    case parse pf s of


    Right (s', g)
    - >

    case parse pv s' of


    Right (s'', a)
    - >
    Right (s'', g a)


    Left e
    - >
    Left e


    Left e
    - >
    Left e

    View Slide

  38. DmitryTsepelev
    🌆
    Реализуем аппликацию
    38
    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    instance Applicative Parser where


    pure a = Parser $ \s
    - >
    Right (s, a)


    pf
    < * >
    pv = Parser $ \s
    - >

    case parse pf s of


    Right (s', g)
    - >

    case parse pv s' of


    Right (s'', a)
    - >
    Right (s'', g a)


    Left e
    - >
    Left e


    Left e
    - >
    Left e

    View Slide

  39. DmitryTsepelev
    🌆
    Реализуем аппликацию
    39
    parse (pure (
    :
    )
    < * >
    char 'A'
    < * >
    string "BC") "ABC"
    - -
    Right ("","ABC")


    parse (char 'A'
    * >
    string "BC") "ABC"
    - -
    Right ("","BC")


    parse (char 'A'
    < *
    string "BC") "ABC"
    - -
    Right ("",'A')

    View Slide

  40. DmitryTsepelev
    🌆
    Класс типов Alternative
    40
    import Control.Applicative hiding (many)


    import Data.Either (isRight)


    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    instance Alternative Parser where


    empty = Parser $ \s
    - >
    Left $ "unexpected "
    + +
    s


    p
    < | >
    q = Parser f where


    f s = let ps = parse p s in if isRight ps then ps else parse q s


    parse (char 'A'
    < | >
    char 'B') "AB"
    - -
    Right ("B",'A')


    parse (char 'A'
    < | >
    char 'B') "BA"
    - -
    Right ("A",'B')

    View Slide

  41. DmitryTsepelev
    🌆
    Комбинаторы
    41
    import Control.Applicative hiding (many)


    import Data.Either (isRight)


    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    instance Alternative Parser where


    empty = Parser $ \s
    - >
    Left $ "unexpected "
    + +
    s


    p
    < | >
    q = Parser f where


    f s = let ps = parse p s in if isRight ps then ps else parse q s


    many
    : :
    Parser a
    - >
    Parser [a]


    many p = pure (
    :
    )
    < * >
    p
    < * >
    many p
    < | >
    pure []


    parse (many (char 'A')) "AAABC"
    - -
    Right ("BC","AAA")


    parse (many (char 'A')) "BC"
    - -
    Right ("BC","")


    many1
    : :
    Parser a
    - >
    Parser [a]


    many1 p = pure (
    :
    )
    < * >
    p
    < * >
    many p


    parse (many1 (char 'A')) "AAABC"
    - -
    Right ("BC","AAA")


    parse (many1 (char 'A')) "BC"
    - -
    Left "unexpected B"


    View Slide

  42. DmitryTsepelev
    🌆
    SQL: select
    42
    whitespace
    : :
    Parser String


    whitespace = many (char ' ')


    sepBy
    : :
    Parser a
    - >
    Parser sep
    - >
    Parser [a]


    sepBy p sep = pure (flip (
    :
    ))
    < $ >
    many (p
    < *
    sep)
    < * >
    p


    alphaNum
    : :
    Parser Char


    alphaNum = satisfy isAlphaNum


    tableNameP
    : :
    Parser String


    tableNameP = many1 (alphaNum
    < | >
    char '.')


    selectP
    : :
    Parser [String]


    selectP = string "select"
    * >
    whitespace
    * >
    (tableNameP `sepBy` (char ','
    < *
    whitespace))


    parse selectP "select movies.id, movies.title"
    - -
    Right ("",["movies.title","movies.id"])

    View Slide

  43. DmitryTsepelev
    🌆
    SQL: from
    43
    fromP
    : :
    Parser String


    fromP = whitespace
    * >
    string "from"
    * >
    whitespace
    * >
    many1 alphaNum


    parse fromP "from movies"
    - -
    Right ("","movies")

    View Slide

  44. DmitryTsepelev
    🌆
    SQL: join
    44
    data Join = Join String String String deriving (Show)


    joinP
    : :
    Parser Join


    joinP =


    Join


    < $ >
    (whitespace
    * >
    string "join"
    * >
    whitespace
    * >
    many1 alphaNum
    < *
    whitespace)


    < * >
    (string "on"
    * >
    whitespace
    * >
    tableNameP
    < *
    whitespace)


    < * >
    (char '='
    * >
    whitespace
    * >
    tableNameP)


    parse joinP "join directors on movies.directorId = directors.id”


    - -
    Right ("",Join "directors" "movies.directorId" "directors.id")

    View Slide

  45. DmitryTsepelev
    🌆
    SQL: joins
    45
    data Join = Join String String String deriving (Show)


    optionMaybe
    : :
    Parser a
    - >
    Parser (Maybe a)


    optionMaybe p =


    Parser $ \s
    - >

    let ps = parse p s in case ps of


    Right (s', a)
    - >
    if s'
    / =
    s then Right (s', Just a) else Right (s', Nothing)


    Left _
    - >
    Right (s, Nothing)


    joinsP
    : :
    Parser (Maybe [Join])


    joinsP = whitespace
    * >
    optionMaybe (many joinP)

    View Slide

  46. DmitryTsepelev
    🌆
    SQL: joins
    46
    let query =


    "join directors on movies.directorId = directors.id


    join moviesActors on moviesActors.movieId = moviesId"


    parse joinsP query


    - -
    Right ("",Just [Join "directors" "movies.directorId"
    "directors.id",Join "moviesActors" "moviesActors.movieId"
    "moviesId"])


    parse joinsP ""


    - -
    Right ("",Nothing)

    View Slide

  47. DmitryTsepelev
    🌆
    SQL
    47
    runParser
    : :
    Parser a
    - >
    String
    - >
    a


    runParser p s | Right ("", a)
    < -
    parse p s = a


    | otherwise = error "failed to run parser"


    data Query = Query { selection
    : :
    [String], from
    : :
    String, joins
    : :
    Maybe [Join] } deriving (Show)


    sqlP
    : :
    Parser Query


    sqlP = Query
    < $ >
    selectP
    < * >
    fromP
    < * >
    joinsP


    runParser sqlP "select movies.title, movies.createdAt from movies join directors on directors.id =
    movies.directorId"


    - -
    Query {selection = ["movies.createdAt","movies.title"], from = "movies", joins = Just [Join
    "directors" "directors.id" "movies.directorId"]}

    View Slide

  48. DmitryTsepelev
    🌆
    Выводы
    • ФП — это весело (да?);


    • все используют функторы;


    • с помощью хитрых операций с функциями можно получать интересное
    поведение;


    • аппликативные функторы позволяют выстраивать цепочки вычислений
    без обработки ошибок в процессе.
    48

    View Slide

  49. DmitryTsepelev
    🌆
    Куда пойти дальше
    • Learn You a Haskell (http:/
    /learnyouahaskell.com);


    • Функциональное программирование на языке Haskell (https:/
    /
    stepik.org/course/75/syllabus).
    49

    View Slide

  50. Спасибо!
    @dmitrytsepelev
    🌎 dmitrytsepelev.dev

    View Slide

  51. Спасибо!
    @dmitrytsepelev
    🌎 dmitrytsepelev.dev
    Надо что–то спросить 🤔
    • что там за эмоджи слева сверху было?
    🌇


    • покажи фокус с долларом


    • почему реализация Applicative для
    парсера была страшная, как смерть
    водолаза?


    • а что там про монады было?


    • а правда ли что главное чтобы типы
    сошлись?

    View Slide

  52. DmitryTsepelev
    🌆
    Фокус с долларом
    52
    zip [1, 3] [2, 4]
    - -
    [[1, 2], [3, 4]]


    zipWith (+) [1, 3] [2, 4]
    - -
    [4, 6]


    [(+4), (*5)] [3, 7]


    как вызвать функции из массива
    слева с аргументом справа?
    sum $ zipWith ($) [(+4), (*5)] [3, 7]
    - -
    42


    sum [(+4) $ 3, (*5) $ 7]

    View Slide

  53. DmitryTsepelev
    🌆
    Реализуем аппликацию
    53
    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    instance Applicative Parser where


    pure a = Parser $ \s
    - >
    Right (s, a)


    pf
    < * >
    pv = Parser $ \s
    - >

    case parse pf s of


    Right (s', g)
    - >

    case parse pv s' of


    Right (s'', a)
    - >
    Right (s'', g a)


    Left e
    - >
    Left e


    Left e
    - >
    Left e

    View Slide

  54. DmitryTsepelev
    🌆
    Applicative Parser на монадах: bind
    54
    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    instance Applicative Parser where


    pure a = Parser $ \s
    - >
    Right (s, a)


    pf
    < * >
    pv = Parser $ \s
    - >

    parse pf s
    > >
    =


    \(s', g)
    - >
    parse pv s'
    > >
    =


    \(s'', a)
    - >
    Right (s'', g a)

    View Slide

  55. DmitryTsepelev
    🌆
    Applicative Parser на монадах: do–нотация
    55
    data Parser a = Parser { parse
    : :
    String
    - >
    Either String (String, a) }


    instance Applicative Parser where


    pure a = Parser $ \s
    - >
    Right (s, a)


    pf
    < * >
    pv = Parser $ \s
    - >
    do


    (s', g)
    < -
    parse pf s


    (s'', a)
    < -
    parse pv s'


    return (s'', g a)

    View Slide

  56. DmitryTsepelev
    🌆
    many вообще не зависит от наших парсеров!
    56
    many
    : :
    Parser a
    - >
    Parser [a]


    many p = (
    :
    )
    < $ >
    p
    < * >
    many p
    < | >
    pure []

    View Slide

  57. DmitryTsepelev
    🌆
    many вообще не зависит от наших парсеров!
    57
    many
    : :
    Alternative t
    = >
    t a
    - >
    t [a]


    many p = (
    :
    )
    < $ >
    p
    < * >
    many p
    < | >
    pure []

    View Slide

  58. DmitryTsepelev
    🌆
    Магия типов: pure f <*> g == f <$> g
    58
    pure (
    :
    )
    < * >
    char 'A'


    (
    :
    )
    : :
    a
    - >
    [a]
    - >
    [a]


    pure
    : :
    a
    - >
    Parser a


    pure
    : :
    Char
    - >
    Parser a


    Parser (Char
    - >
    [Char]
    - >
    [Char])
    < * >
    Parser Char


    (
    < * >
    )
    : :
    Parser (a
    - >
    b)
    - >
    Parser a
    - >
    Parser b


    (
    < * >
    )
    : :
    Parser (Char
    - >
    [Char])
    - >
    Parser Char
    - >
    Parser [Char]


    Parser ([Char]
    - >
    [Char])


    fmap
    : :
    (a
    - >
    b)
    - >
    Parser a
    - >
    Parser b


    fmap
    : :
    (Char
    - >
    [Char])
    - >
    Parser Char
    - >
    Parser [Char]


    (Char
    - >
    [Char]
    - >
    [Char])
    < $ >
    Parser Char


    Parser ([Char]
    - >
    [Char])


    (
    :
    )
    < $ >
    char 'A'

    View Slide