$30 off During Our Annual Pro Sale. View Details »

Redneck Monads

Redneck Monads

My talk on rannts#10. Yet another attempt to convince that monads are simple and natural thing to use everyday.

Sergey Arkhipov

April 23, 2016
Tweet

More Decks by Sergey Arkhipov

Other Decks in Programming

Transcript

  1. Сергей Архипов, @9seconds
    23.04.2016
    >>=
    Колхозные монады

    View Slide

  2. View Slide

  3. View Slide

  4. Монада — это тройка (m, return, >>=)
    class Monad m where
    (>>=) :: m a → (a → m b) → m b
    return :: a → m a
    Законы монад
    1. (return x >>= f) = f x
    2. ((x >>= f) >>= g) = (x >>= (\y →
    f y >>= g))
    3. (x >>= return) = x
    (x >>= (\a → return a)) = x

    View Slide

  5. def generator(values):
    for value in values:
    yield value
    >>> print list(generator([1, 2, 3]))
    ... [1, 2, 3]

    View Slide

  6. def chain(iterable):
    for values in iterable:
    for value in values:
    yield value
    >>> print list(chain([(1, 2), (3, 4)]))
    ... [1, 2, 3, 4]

    View Slide

  7. >>> m = map(lambda x: x * 2, [1, 2, 3]))
    >>> print list(m)
    ... [2, 4, 6]

    View Slide

  8. >>> m = map(lambda x: x * 2, ([1], [2], [3]))
    >>> print list(m)
    ... [[1, 1], [2, 2], [3, 3]]

    View Slide

  9. >>> m = map(lambda x: x * 2, ([1], [2], [3]))
    >>> print list(m)
    ... [[1, 1], [2, 2], [3, 3]]
    >>> print list(chain(m))
    ... [1, 1, 2, 2, 3, 3]

    View Slide

  10. def chainmap(func, iterable):
    return chain(map(func, iterable))
    >>> m = chainmap(lambda x: x * 2, ([1], [2], [3]))
    >>> print list(m)
    ... [1, 1, 2, 2, 3, 3]

    View Slide

  11. class list(list):
    def chainmap(self, func):
    res = chainmap(func, self)
    return list(res)

    View Slide

  12. >>> l = list(['1460401415', '146040000', '146020000'])
    >>> res1 = l.chainmap(lambda x: [int(x)])
    >>> print res1
    ... [1460401415, 146040000, 146020000]

    View Slide

  13. >>> l = list(['1460401415', '146040000', '146020000'])
    >>> res1 = l.chainmap(lambda x: [int(x)])
    >>> print res1
    ... [1460401415, 146040000, 146020000]
    >>> fn = lambda x: [datetime.fromtimestamp(x)]
    >>> res2 = res1.chainmap(fn)
    ... [datetime.datetime(…), …]

    View Slide

  14. >>> l = list(['1460401415', '146040000', '146020000'])
    >>> res1 = l.chainmap(lambda x: [int(x)])
    >>> print res1
    ... [1460401415, 146040000, 146020000]
    >>> fn = lambda x: [datetime.fromtimestamp(x)]
    >>> res2 = res1.chainmap(fn)
    ... [datetime.datetime(…), …]
    >>> fn2 = lambda x: [x.strftime('%H')]
    >>> res3 = res2.chainmap(fn2)
    ... ['22', '09', '04']

    View Slide

  15. Класс list:
    ― Класс-контейнер;
    ― Метод chainmap, который применяет
    функцию func для каждого элемента
    контейнера;
    ― Функция func берет элемент x и
    возвращает экземпляр list с одним или
    несколькими значениями;
    ― Метод chainmap сливает списки в
    экземпляр класса list;

    View Slide

  16. Класс list:
    ― Класс-контейнер;
    ― Метод chainmap, который применяет
    функцию func для каждого элемента
    контейнера;
    ― Функция func берет элемент x и
    возвращает экземпляр list с одним или
    несколькими значениями;
    ― Метод chainmap сливает списки в
    экземпляр класса list;
    Это монада

    View Slide

  17. Монадический тип:
    ― Класс-контейнер;
    ― Функция связывания, которая берет
    элемент из контейнера и делает из него
    новый контейнер;
    ― Тип умеет брать контейнеры, полученные в
    результате работы функции связывания и
    организовывать новый.

    View Slide

  18. Монада — это тройка (m, return, >>=)
    class Monad m where
    (>>=) :: m a → (a → m b) → m b
    return :: a → m a
    Законы монад
    1. (return x >>= f) = f x
    2. ((x >>= f) >>= g) = (x >>= (\y →
    f y >>= g))
    3. (x >>= return) = x

    View Slide

  19. ((x >>= f) >>= g) = (x >>= (\y →
    f y >>= g))
    res = list([a]) \
    .chainmap(fn1).chainmap(fn2)
    res2 = list([a]).chainmap(
    lambda x: fn1(x).chainmap(fn2))
    assert res == res2

    View Slide

  20. Связывание и ленивость
    >>> from itertools import imap, ifilter
    >>> items = imap(lambda x: 10000 * x, items)
    >>> items = ifilter(lambda x: x % 17 == 5, items)
    >>> items = imap(datetime.fromtimestamp, items)
    >>> items = list(items)

    View Slide

  21. def read_and_parse(filename):
    filefp = open(filename, 'r')
    content = filefp.read()
    parsed = parse(content)
    filefp.read_finished()
    filefp.complete()
    return parsed

    View Slide

  22. def read_and_parse(filename):
    try:
    filefp = open(filename, 'r')
    except:
    return None
    try:
    return parse(filefp.read())
    finally:
    filefp.close()

    View Slide

  23. def read_and_parse(filename):
    filefp = content = parsed = None
    try:
    filefp = open(filename, 'r')
    content = filefp.read()
    return parse(content)
    except:
    pass
    finally:
    if filefp is not None:
    filefp.complete()

    View Slide

  24. class Maybe:
    def __init__(self, value):
    self.value = value
    def bind(self, func):
    if isinstance(self, Nothing):
    return self
    try:
    value = func(self.value)
    except:
    return Nothing()
    return Some(value)
    class Some(Maybe):
    pass
    class Nothing(Maybe):
    def __init__(self, *args):
    super().__init__(None)

    View Slide

  25. def monad_read_and_parse(filename):
    fname = Maybe(filename)
    filefp = fname.bind(
    lambda v: Some(open(v, 'r')))
    content = filefp.bind(
    lambda v: Some(v.read()))
    parsed = content.bind(parse).value
    filefp.bind(
    lambda v: Some(v.close()))
    return parsed

    View Slide

  26. enum Option {
    None,
    Some(T),
    }
    enum Result {
    Ok(T),
    Err(E),
    }

    View Slide

  27. use std::env;
    fn main() {
    let mut argv = env::args();
    let arg: String = argv.nth(1).unwrap();
    let n: i32 = arg.parse().unwrap();
    println!("{}", 2 * n);
    }

    View Slide

  28. use std::env;
    fn double_arg(mut argv: env::Args) -> Result {
    argv.nth(1)
    .ok_or(
    "Please give at least one argument".to_owned())
    .and_then(|arg| arg.parse::().map_err(
    |err| err.to_string()))
    .map(|n| 2 * n)
    }
    fn main() {
    match double_arg(env::args()) {
    Ok(n) => println!("{}", n),
    Err(err) => println!("Error: {}", err),
    }
    }

    View Slide

  29. use std::fs::File;
    use std::io::Read;
    use std::path::Path;
    fn file_double>(file_path: P) -> i32 {
    let mut file = File::open(file_path).unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    let n: i32 = contents.trim().parse().unwrap();
    2 * n
    }
    fn main() {
    let doubled = file_double("foobar");
    println!("{}", doubled);
    }

    View Slide

  30. use std::fs::File;
    use std::io::Read;
    use std::path::Path;
    fn file_double>(file_path: P) -> Result {
    File::open(file_path)
    .map_err(|err| err.to_string())
    .and_then(|mut file| {
    let mut contents = String::new();
    file.read_to_string(&mut contents)
    .map_err(|err| err.to_string())
    .map(|_| contents)
    })
    .and_then(|contents| {
    contents.trim().parse::()
    .map_err(|err| err.to_string())
    })
    .map(|n| 2 * n)
    }
    fn main() {
    match file_double("foobar") {
    Ok(n) => println!("{}", n),
    Err(err) => println!("Error: {}", err),
    }
    }

    View Slide

  31. use std::fs::File;
    use std::io::Read;
    use std::path::Path;
    fn file_double>(file_path: P) -> Result {
    let mut file = try!(
    File::open(file_path).map_err(|e| e.to_string()));
    let mut contents = String::new();
    try!(file.read_to_string(&mut contents).map_err(
    |e| e.to_string()));
    let n = try!(contents.trim().parse::().map_err(
    |e| e.to_string()));
    Ok(2 * n)
    }
    fn main() {
    match file_double("foobar") {
    Ok(n) => println!("{}", n),
    Err(err) => println!("Error: {}", err),
    }
    }

    View Slide

  32. var text = httpGet(
    url.parse("http://example.biz")
    ).then(
    function (res) {
    return httpGet(
    url.parse(res.headers["location"]));
    }
    ).then(loadBody).fail(function (error) {
    return "Not Found";
    });
    text.then(console.log);
    text.post("toUpperCase").then(console.log);
    text.post("toLowerCase").then(console.log);

    View Slide

  33. class MessageLog:
    def __init__(self, value, message=None):
    self.messages = []
    if message is not None:
    self.messages.append(message)
    self.value = value
    def bind(self, func):
    If isinstance(self, Nothing):
    return self
    try:
    value = func(self.value)
    except Exception as e:
    value = Nothing
    if isinstance(value, Nothing):
    return value
    monad = MessageLog(value.value)
    monad.messages = self.messages + value.messages
    return monad

    View Slide

  34. @9seconds   
    https:/
    /speakerdeck.com/9seconds/redneck-monads
    Lenses are coalgebras
    for the costate comonad

    View Slide