Slide 1

Slide 1 text

函数式 Ruby 编程 欧阳继超

Slide 2

Slide 2 text

Agenda 什么是函数式编程 Ruby 的一些函数式特性 使用 Monad 纯化/ 简化控制流

Slide 3

Slide 3 text

FBI Warning 用 Nokia 的同学请自己手动输入 git.io/fprb

Slide 4

Slide 4 text

我是...

Slide 5

Slide 5 text

我是...

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

我还是

Slide 9

Slide 9 text

啊? 所以是... 一个会点 Scala 的 JavaScript 程序员来教 Rubyist 函数式编程?

Slide 10

Slide 10 text

我在 JavaScript 社区讲函数式的时候, 观众是这样 的...

Slide 11

Slide 11 text

Ruby 函数式 ? https://cloud.githubusercontent.com/assets/1235045/17933311/321ff7d2-6a46-11e6-903e- 8cc84f3acc78.JPG You might be surprised to see Ruby in the list of functional languages because they generally count as object oriented languages. -- Martin Odersky 你可能奇怪我把Ruby 也放到了函数式语言的列表, 这些语言通常会被归到面向对 象语言. -- Scala 之父

Slide 12

Slide 12 text

什么是函数式 一等函数 first class function / 入 lambda 纯 purity 引用透明性 referential transparency 无副作用 side effectless 不可变 immutability 持久化数据结构 persistent data structures ... 当纯到一定程度可能就需要 范畴论 Catergory Theory

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

好处呢? 好组合 composible 好推理 easy to reason about 好测试 easy to test 好多线程 Multi-thread 好玩 fun 好 高逼格 high bigger elegant

Slide 15

Slide 15 text

你可能不知道的Ruby

Slide 16

Slide 16 text

lambda aka 匿名函数 [ 多选题] 请选出所有的 lambda A : { } / d o e n d # s u c h a s = [ 1 , 2 , 3 ] . m a p { | x | x + 1 } = B : p l u s 1 = l a m b d a { | x | x + 1 } C : p l u s 1 = - > ( x ) { x + 1 } D : p l u s 1 = P r o c . n e w { | x | x + 1 }

Slide 17

Slide 17 text

万物皆对象, lambda 也不例外 lambda 也就是一个正常的对象 p l u s 1 = - > ( x ) { x + 1 }

Slide 18

Slide 18 text

如果给这个lambda 一个引用, 我们可以跟用 method 一样用 p l u s 1 = - > ( x ) { x + 1 } p l u s 1 . c a l l ( 3 ) p l u s 1 . ( 3 ) p l u s 1 [ 3 ]

Slide 19

Slide 19 text

三等公民 d e f p l u s 1 x x + 1 e n d [ 1 , 2 , 3 , 4 ] . m a p & p l u s 1 ` p l u s 1 ' : w r o n g n u m b e r o f a r g u m e n t s ( 0 f o r 1 ) ( A r g u m e n t E r r o r )

Slide 20

Slide 20 text

一等 vs 三等

Slide 21

Slide 21 text

一等公民 Proc p l u s 1 = - > ( x ) { x + 1 } [ 1 , 2 , 3 , 4 ] . m a p & p l u s 1

Slide 22

Slide 22 text

给三等座升个舱 d e f p l u s 1 x x + 1 e n d f i r s t _ c l a s s _ p l u s 1 = m e t h o d ( : p l u s 1 ) [ 1 , 2 , 3 , 4 ] . m a p & f i r s t _ c l a s s _ p l u s 1

Slide 23

Slide 23 text

升舱的魔法 # t o _ p r o c m e t h o d ( : p l u s 1 ) # = > # < M e t h o d : O b j e c t # p l u s 1 > c l a s s M e t h o d d e f t o _ p r o c l a m b d a { | * a r g s | s e l f . c a l l ( * a r g s ) } e n d e n d

Slide 24

Slide 24 text

升舱实例2 - Symbol % w ( o u y a n g j i c h a o ) . m a p & : c a p i t a l i z e # = = = % w ( o u y a n g j i c h a o ) . m a p { | x | x . c a p i t a l i z e }

Slide 25

Slide 25 text

来 Desugar & % w ( o u y a n g j i c h a o ) . m a p & : c a p i t a l i z e . t o _ p r o c % w ( o u y a n g j i c h a o ) . m a p & P r o c . n e w ( | x | x . s e n d ( : c a p i t a l i z e ) )

Slide 26

Slide 26 text

模式匹配 pattern matching

Slide 27

Slide 27 text

destructure - 数组 f i r s t , * m i d d l e _ a n d _ l a s t = [ ' P h i l l i p ' , ' J a y ' , ' F r y ' ] " f i r s t : # { f i r s t } , m i d d l e _ a n d _ l a s t : # { m i d d l e _ a n d _ l a s t } "

Slide 28

Slide 28 text

destructure - 哈希 方法的参数会自带 destructure 哈希的功能 aka k e y w o r d a r g u m e n t s : f r y = { f i r s t : ' P h i l l i p ' , m i d d l e : ' J a y ' , l a s t : ' F r y ' } d e f p r i n t F i r s t N a m e f i r s t : , * * r e s t p f i r s t , r e s t e n d p r i n t F i r s t N a m e f r y

Slide 29

Slide 29 text

case when ruby 中的 case 可以搞定这几种模式匹配 值/ 表达式 类型 Proc 正则

Slide 30

Slide 30 text

值 这个很简单,应该都有用过 m e = ' o u y a n g ' c a s e m e w h e n ' o u y a n g ' " h e h e # { m e } " e l s e ' h e h e j i c h a o ' e n d

Slide 31

Slide 31 text

类型 c l a s s M e d e f i n i t i a l i z e n a m e @ n a m e = n a m e e n d d e f h e h e d a " 呵呵哒 # { @ n a m e } " e n d e n d m e = M e . n e w ' o u y a n g ' c a s e m e w h e n M e m e . h e h e d a e l s e ' 呵呵哒了' e n d

Slide 32

Slide 32 text

lambda (aka guard ) r e q u i r e ' o s t r u c t ' m e = O p e n S t r u c t . n e w ( n a m e : ' j i c h a o ' , f i r s t _ n a m e : ' o u y a n g ' ) c a s e m e w h e n - > ( w h o ) { w h o . n a m e = = ' j i c h a o ' } " h e h e # { m e } " e n d

Slide 33

Slide 33 text

正则 c a s e ' j i c h a o o u y a n g ' w h e n / o u y a n g / " 呵呵哒" e n d

Slide 34

Slide 34 text

但其实只是个简单的语法糖 case when 并不是magic ,其实只是 if else 的语法糖, 比如上面说的正则 i f ( / o u y a n g / = = = ' j i c h a o ' ) " h e h e d a " e n d 所以 magic 则是所有 when 的对象都实现了 = = = 方法而已 值: o b j e c t . = = = 会代理到 = = 类型: M o d u l e . = = = 会看是否是其 instance 正则: r e g e x . = = = 如果匹配返回 true 表达式:取决于表达式返回的值的 = = = 方法 lambda : p r o c . = = = 会运行 lambda 或者 proc

Slide 35

Slide 35 text

说了这么些奇技淫巧, 逼格还是不够高呀 除了花式一些有什么用呢? 纯 pure

Slide 36

Slide 36 text

Category Theory Monad - 自函子范畴上的含幺半群

Slide 37

Slide 37 text

一个简单 把大象放冰箱里需要几步

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

命令式放大象 o p e n e d _ f r i d g e = o p e n _ f r i d g e i f o p e n e d _ f r i d g e f r i d g e _ w _ e l e p h e n t = p u t _ e l e p h e n t _ i n o p e n e d _ f r i d g e i f f r i d g e _ w _ e l e p h e n t c l o s e d _ f r i d g e = c l o s e _ f r i d g e i f c l o s e d _ f r i d g e ' y a y ' e l s e ' f a i l t o c l o s e f r i d g e ' e n d e l s e ' f a i l t o p u t e l e p h e n t i n ' e n d e l s e ' f a i l t o o p e n f r i d g e ' e n d

Slide 41

Slide 41 text

监控 o p e n e d _ f r i d g e = o p e n _ f r i d g e i f o p e n e d _ f r i d g e M o n i t o r i n g . l o g g e r . i n f o ( ' f r i d g e o p e n e d ' ) f r i d g e _ w _ e l e p h e n t = p u t _ e l e p h e n t _ i n o p e n e d _ f r i d g e i f f r i d g e _ w _ e l e p h e n t M o n i t o r i n g . l o g g e r . i n f o ( ' p u t e d a e l e p h e n t i n t o f r i d g e ' ) c l o s e d _ f r i d g e = c l o s e _ f r i d g e i f c l o s e d _ f r i d g e M o n i t o r i n g . l o g g e r . i n f o ( ' f r i d g e c l o s e d ' ) ' y a y ' e l s e M o n i t o r i n g . l o g g e r . e r r o r ( ' n o a b l e t o c l o s e f r i d g e ' ) ' f a i l t o c l o s e f r i d g e ' e n d e l s e M o n i t o r i n g . l o g g e r . e r r o r ( ' e l e p h e n t p u t f a i l e d ' ) ' f a i l t o p u t e l e p h e n t i n ' e n d e l s e M o n i t o r i n g . l o g g e r . e r r o r ( ' f a i l t o o p e n f r i d g e ' ) ' f a i l t o o p e n f r i d g e ' e n d

Slide 42

Slide 42 text

或者用更极端的抛异常方式 b e g i n c l o s e ( p u t _ e l e p h e n t _ i n o p e n _ f r i d g e ) r e s c u e A = > e . . . r e s c u e B = > e . . . r e s c u e C = > e . . . e n d

Slide 43

Slide 43 text

广告时间 ➡ ⬇ ↘ ⬇ ➡ 猫呢? https://git.io/cats.rb

Slide 44

Slide 44 text

让我们用一个简单的 Either Monad g e m i n s t a l l d a t a . e i t h e r r e q u i r e ' d a t a . e i t h e r ' R i g h t . n e w ( 1 ) . f l a t _ m a p d o | x | i f x < 1 L e f t . n e w ( ' m e h ' ) e l s e R i g h t . n e w ( x + 1 ) e n d e n d # = > # < R i g h t 2 >

Slide 45

Slide 45 text

来简化控制流 o p e n _ f r i d g e . f l a t _ m a p d o | f r i d g e | # < = 1 p u t _ e l e p h e n t _ i n f r i d g e # < = 2 e n d . f l a t _ m a p d o | f r i d g e | c l o s e f r i d g e # < = 3 e n d 这样可以专心构造控制逻辑, 而不需要关心上一步如果错误该怎么办

Slide 46

Slide 46 text

怎么做到的

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

Either 魔法 https://github.com/jcouyang/cats.rb/blob/master/lib/data.either.rb d e f f l a t _ m a p c a s e s e l f w h e n R i g h t y i e l d @ v e l s e s e l f e n d e n d

Slide 49

Slide 49 text

一个更实际的 用 microservices 组合成新的 service

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

上图有几次 IO 总共4 个IO, 每一步骤都可能出错 但程序猿不希望漏掉任何错误信息 但是又不能为了监控, 影响了这个简单的工作流

Slide 53

Slide 53 text

控制流不关心失败和监控 d o a < - f e t c h A b < - f e t c h B c < - p u t $ b l a h a + + b

Slide 54

Slide 54 text

IO 自挂东南枝

Slide 55

Slide 55 text

d e f f e t c h ( e n d p o i n t , d e c o d e r ) r e s p o n s e = s e l f . c l a s s . g e t ( e n d p o i n t , f o r m a t : : j s o n ) c a s e r e s p o n s e . c o d e w h e n 4 1 0 L e f t . n e w ( E x c e p t i o n s : : D a t a F a i l u r e . n e w ( " R e s o u r c e # { e n d p o i n t } w a s d e l e t e d " ) ) w h e n 4 0 4 L e f t . n e w ( E x c e p t i o n s : : D a t a F a i l u r e . n e w ( " R e s o u r c e # { e n d p o i n t } n o t e x i s t " ) ) w h e n 2 0 0 R i g h t . n e w d e c o d e r . f r o m _ j s o n ( r e s p o n s e . b o d y ) e l s e L e f t . n e w ( E x c e p t i o n s : : R e p o s i t o r y E r r o r . n e w ( " F e t c h i n g # { e n d p o i n t } w i t h E r r o r : \ n # { e n d p o i n t } , r e s p o n s e c o d e n d e n d

Slide 56

Slide 56 text

f a i l u r e _ p r o c e s s e d , s u c c e s s _ p r o c e s s e d = E i t h e r . p a r t i t i o n M a p i n a t o r . r u n M o n i t o r i n g . s e n d _ p r o c e s s e d s u c c e s s _ p r o c e s s e d . l e n g t h M o n i t o r i n g . l o g g e r . i n f o ( " P r o c e s s e d s u c c e s s f u l # { s u c c e s s _ p r o c e s s e d . l e n g t h } l i s t i n g s : # { s u c c e s s _ p r o c e s s e d } M o n i t o r i n g . l o g g e r . e r r o r ( " P r o c e s s e d F A I L U R E # { f a i l u r e _ p r o c e s s e d . l e n g t h } w i t h E x c e p t i o n s : " ) u n l e s s f a i l u r e . . .

Slide 57

Slide 57 text

还可不可以在纯一些

Slide 58

Slide 58 text

Free Monad aka Interpreter Pattern

Slide 59

Slide 59 text

有些像 Cons

Slide 60

Slide 60 text

还有... Coyoneda Free Monoid State EitherT MaybeT ...

Slide 61

Slide 61 text

这些我都不会讲...

Slide 62

Slide 62 text

因为我不会讲... 希望不久之后可以... g e m i n s t a l l c o n t r o l . m o n a d . f r e e

Slide 63

Slide 63 text

Q/A

Slide 64

Slide 64 text

性能 你TM 都选 Ruby 了还在乎性能?

Slide 65

Slide 65 text

并发多线程 made easy r e q u i r e " c e l l u l o i d / a u t o s t a r t " m o d u l e E n u m e r a b l e d e f p m a p ( & b l o c k ) f u t u r e s = m a p { | e l e m | C e l l u l o i d : : F u t u r e . n e w ( e l e m , & b l o c k ) } f u t u r e s . m a p ( & : v a l u e ) e n d e n d

Slide 66

Slide 66 text

多谢 https://blog.oyanglul.us/functional-ruby.html https://github.com/jcouyang/cats.rb https://github.com/typelevel/cats http://hackage.haskell.org/package/base-4.8.1.0/docs/src/Data.Either.html https://wiki.haskell.org/Free_structure http://underscore.io/blog/posts/2015/04/23/deriving-the-free-monad.html http://underscore.io/blog/posts/2015/04/14/free-monads-are-simple.html https://www.coursera.org/learn/progfun2 https://www.amazon.com/Well-Grounded-Rubyist-David-Black/dp/1933988657 https://mitpress.mit.edu/sicp