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

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 >>= Колхозные монады

  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