Sergey Arkhipov
April 23, 2016
78

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

April 23, 2016

## Transcript

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

list(generator([1, 2, 3])) ... [1, 2, 3]
4. ### def chain(iterable): for values in iterable: for value in values:

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

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

[3])) >>> print list(m) ... [[1, 1], [2, 2], [3, 3]]
7. ### >>> 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]
8. ### 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]

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

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

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

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

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

= parse(content) filefp.read_finished() filefp.complete() return parsed

21. ### 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()
22. ### 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)
23. ### 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
24. ### enum Option<T> { None, Some(T), } enum Result<T, E> {

Ok(T), Err(E), }
25. ### 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); }
26. ### 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), } }
27. ### 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); }
28. ### 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), } }
29. ### 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), } }