Save 37% off PRO during our Black Friday Sale! »

Redneck Monads

Redneck Monads

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

A8d8ca813a744866b9f85ea1cefb5813?s=128

Sergey Arkhipov

April 23, 2016
Tweet

Transcript

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

  2. None
  3. None
  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
  5. def generator(values): for value in values: yield value >>> print

    list(generator([1, 2, 3])) ... [1, 2, 3]
  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]
  7. >>> m = map(lambda x: x * 2, [1, 2,

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

    [3])) >>> print list(m) ... [[1, 1], [2, 2], [3, 3]]
  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]
  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]
  11. class list(list): def chainmap(self, func): res = chainmap(func, self) return

    list(res)
  12. >>> l = list(['1460401415', '146040000', '146020000']) >>> res1 = l.chainmap(lambda

    x: [int(x)]) >>> print res1 ... [1460401415, 146040000, 146020000]
  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(…), …]
  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']
  15. Класс list: ― Класс-контейнер; ― Метод chainmap, который применяет функцию

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

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

    из контейнера и делает из него новый контейнер; ― Тип умеет брать контейнеры, полученные в результате работы функции связывания и организовывать новый.
  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
  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
  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)
  21. def read_and_parse(filename): filefp = open(filename, 'r') content = filefp.read() parsed

    = parse(content) filefp.read_finished() filefp.complete() return parsed
  22. def read_and_parse(filename): try: filefp = open(filename, 'r') except: return None

    try: return parse(filefp.read()) finally: filefp.close()
  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()
  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)
  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
  26. enum Option<T> { None, Some(T), } enum Result<T, E> {

    Ok(T), Err(E), }
  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); }
  28. use std::env; fn double_arg(mut argv: env::Args) -> Result<i32, String> {

    argv.nth(1) .ok_or( "Please give at least one argument".to_owned()) .and_then(|arg| arg.parse::<i32>().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), } }
  29. use std::fs::File; use std::io::Read; use std::path::Path; fn file_double<P: AsRef<Path>>(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); }
  30. use std::fs::File; use std::io::Read; use std::path::Path; fn file_double<P: AsRef<Path>>(file_path: P)

    -> Result<i32, String> { 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::<i32>() .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), } }
  31. use std::fs::File; use std::io::Read; use std::path::Path; fn file_double<P: AsRef<Path>>(file_path: P)

    -> Result<i32, String> { 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::<i32>().map_err( |e| e.to_string())); Ok(2 * n) } fn main() { match file_double("foobar") { Ok(n) => println!("{}", n), Err(err) => println!("Error: {}", err), } }
  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);
  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
  34. @9seconds    https:/ /speakerdeck.com/9seconds/redneck-monads Lenses are coalgebras for

    the costate comonad