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. Монада — это тройка (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
  2. def chain(iterable): for values in iterable: for value in values:

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

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

    [3])) >>> print list(m) ... [[1, 1], [2, 2], [3, 3]]
  5. >>> 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]
  6. 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]
  7. >>> l = list(['1460401415', '146040000', '146020000']) >>> res1 = l.chainmap(lambda

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

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

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

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

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

    try: return parse(filefp.read()) finally: filefp.close()
  18. 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()
  19. 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)
  20. 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
  21. 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); }
  22. 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), } }
  23. 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); }
  24. 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), } }
  25. 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), } }
  26. 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);
  27. 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