那些 Functional Programming 教我的事

  • 18,442 views
Uploaded on

OSDC.TW 2012

OSDC.TW 2012

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • 非常棒
    Are you sure you want to
    Your message goes here
  • 基本的FP概念都涉及到了,可惜文中涉及Clojure的代码不多,估计是作者不熟悉Clojure吧,但Clojure继承了Lisp很强大的Macro功能,这是其他语言所不能比拟的,希望作者继续提供高质量的presentation.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
18,442
On Slideshare
0
From Embeds
0
Number of Embeds
10

Actions

Shares
Downloads
407
Comments
2
Likes
139

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 那些 Functional Programming 教我的事 ihower@gmail.com 2012/4/15 OSDC.TWSunday, April 15, 12
  • 2. About Me • 張文鈿 a.k.a. ihower • Twitter: @ihower • Ruby 熟手、Scala 初心者 • Ruby on Rails 源碼貢獻者 • 熱情豆行動樂活科技Sunday, April 15, 12
  • 3. Agenda • 1. 為什麼想學FP? • 2. FP有什麼特色?Sunday, April 15, 12
  • 4. Part1: 為什麼想學 Functional ProgrammingSunday, April 15, 12
  • 5. Moore’s Law Ending 單核時脈無法再提昇了,多核架構是現在進行式Sunday, April 15, 12
  • 6. Single-Threading can’t scale! 4 CoresSunday, April 15, 12
  • 7. Horizontal scaling Q: 你的程式 Concurrency Ready 嗎?Sunday, April 15, 12
  • 8. Multithreading Program Demo Example from: What All Rubyist Should Know About Threads (Jim Weirich, RubyConf 2008)Sunday, April 15, 12
  • 9. Thread-safe (1) • 每個存取到的共享變數,都要 SynchronizeSunday, April 15, 12
  • 10. Thread safe (2) • 操作必須注意 AtomicSunday, April 15, 12
  • 11. Thread safe (3) • 要有個策略可以避免 DeadlockSunday, April 15, 12
  • 12. Thread safe (4) • 每個你用到的 Library 都必須滿足上述規 則 (1)~(3)Sunday, April 15, 12
  • 13. 另⼀一個問題... • Synchronized 太多了,都是 blocking threads,程式同時間只有⼀一個 thread 在 跑 :(Sunday, April 15, 12
  • 14. Multi-threading Programing is HardSunday, April 15, 12
  • 15. Hard to program 只好讓團隊中最聰明的人去寫Sunday, April 15, 12
  • 16. Hard to test 測試無法窮舉各種情況,上線後不定時爆炸Sunday, April 15, 12
  • 17. 會這麼難的根本原因: Mutable shared stateSunday, April 15, 12
  • 18. 如果有⼀一種程式語言 沒有 Mutable 變數Sunday, April 15, 12
  • 19. 沒有 Mutable 變數, 那就不需要擔心 Synchronize 問題啦!!Sunday, April 15, 12
  • 20. Functional Programing 早在還沒有多核CPU的50年前Sunday, April 15, 12
  • 21. (Wikipedia) In computer science, functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data. It emphasizes the application of functions, in contrast to the imperative programming style, which emphasizes changes in state.Sunday, April 15, 12
  • 22. (Wikipedia) In computer science, functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data. It emphasizes the application of functions, in contrast to the imperative programming style, which emphasizes changes in state.Sunday, April 15, 12
  • 23. Functional Language 提供了開發 Concurrency program 的良好基礎Sunday, April 15, 12
  • 24. 目前有哪些主流 Functional Language?Sunday, April 15, 12
  • 25. Erlang • Ericsson Functional Language • No mutable variable and side effects • For distributed, reliable, soft real-time highly concurrent systems. • Use the Actor model of concurrencySunday, April 15, 12
  • 26. Erlang 業界案例 • Facebook chat • Github • RabbitMQ • NoSQL • CouchDB, Riak, MembaseSunday, April 15, 12
  • 27. Clojure • Lisp-like functional language • JVM • Use STM (Software transactional memory) for concurrency • 記憶體層級的 ACI (Atomicity, Consistent, Isolation)Sunday, April 15, 12
  • 28. • Hybrid: Object-orient and functional • JVM • Use Akka (Actor and STM) for concurrencySunday, April 15, 12
  • 29. Scala 業界案例 • Twitter • LinkedInSunday, April 15, 12
  • 30. F# • Microsoft’s functional language • .NET • OCaml-likeSunday, April 15, 12
  • 31. Haskell • Pure Functional language 研究領域上的統⼀一 標準實作 • 紀念數學家 Haskell CurrySunday, April 15, 12
  • 32. 小結 • 多核時代,需要能 Concurrency 的程式 • 用程序性的OO語言寫 Concurrency 很難 • Functional Programing 讓 Concurrency Programming 變簡單Sunday, April 15, 12
  • 33. Part2: FP 的幾個特色Sunday, April 15, 12
  • 34. • 1. Avoiding Mutable State • 2. Recursion • 3. Higher-Order Functions • 4. Function Composition • 5. Lazy Evaluation • 6. Pattern MatchingSunday, April 15, 12
  • 35. 1. 避免 Mutable StateSunday, April 15, 12
  • 36. 在FP語言中,變數指 派之後就是 immutable ** Erlang R15B ** X = 1234. X = 5678. ** exception error: no match of right hand side value 5678Sunday, April 15, 12
  • 37. 在 Scala 中,使用 val 宣告變數 val a = 1234 a = 5678 <console>:8: error: reassignment to val var b = 1234 b = 5678 5678Sunday, April 15, 12
  • 38. Persistent data structure • Immutable 的 Map, Hash, Set 等等 • 跟資料庫無關,而是因為其內部實作特性 • Produce new values by sharing structure with existing valuesSunday, April 15, 12
  • 39. Why need Immutable data structure? • No side-effects 的效能衝擊!! • Copying overheadSunday, April 15, 12
  • 40. Linked List 相加兩個List,會共享已有的資料Sunday, April 15, 12
  • 41. Tree (內部實作) 建立新的Tree時,共享資料Sunday, April 15, 12
  • 42. Scala’s collection • 預設提供 Immutable 資料結構 • List • Map • Vector • Set • Mutable 的資料結構需要額外 import • scala.collection.mutable.LinkedList • scala.collection.mutable.Map • scala.collection.mutable.ArrayBuffer • scala.collection.mutable.SetSunday, April 15, 12
  • 43. 2. Recursion 遞迴只應天上有,凡人應當用迴圈?Sunday, April 15, 12
  • 44. Non-recursion 需要 result 變數 def factorial(n) result = 1 # result 是變數 for i in 2..n result = i * result end return result endSunday, April 15, 12
  • 45. Recursion 用 recursion 可以不需要中間變數 def factorial(n) if (n==1) return 1 else return n * factorial(n-1) end endSunday, April 15, 12
  • 46. Recursion? 但是 call stack 爆炸了? > factorial(10000) # SystemStackError: stack level too deepSunday, April 15, 12
  • 47. Tail-call optimization FP程式語言會負責轉成 loop (例:Scala) @tailrec def factorial(acc: Int, n: Int): Int = { if (n <= 1) acc else factorial(n * acc, n - 1) }Sunday, April 15, 12
  • 48. 3. Higher-Order function 的應用Sunday, April 15, 12
  • 49. Function is first-class value ( JavaScript 例) var sayHello = function(){ alert("Hello World!"); }; sayHello();Sunday, April 15, 12
  • 50. Higher-order function • 可以把 function 做參數傳遞到 function 裡 • function 可以當做 function 回傳的結果Sunday, April 15, 12
  • 51. JavaScript (ECMA5) forEach [2,3,5,6].forEach( function(item){ console.log(item); } ); // 2 // 3 // 5 // 6Sunday, April 15, 12
  • 52. Callback method ( Java 需要用 Anonymous inner class) final Button button = new Button(this); button.setOnClickListener( new View.OnClickListener() { public void onClick(View v) { "Hello, World!" }} );Sunday, April 15, 12
  • 53. JDK 8 開始支援 lambda final Button button = new Button(this); button.setOnClickListener( (View v) -> { "Hello" } );Sunday, April 15, 12
  • 54. Combinator Functions • 操作 Collection 的基本三招 • filter 去除某些元素,回傳新的容器 • map 轉變每個元素,回傳新的容器 • fold 迭代計算每個元素,回傳結果Sunday, April 15, 12
  • 55. 範例:找出 Tickets 價格小於1000中,最高的價格 val tickets = List("a","b", "c") def getPrice(ticket : String) = { Map( "a" -> 1100, "b" -> 900, "c" -> 800 ). get(ticket).getOrElse(0) } def Less1000(price: Int) = price < 1000 def pickHighPriced(price1: Int, price2: Int) = { if (price1 > price2) price1 else price2 }Sunday, April 15, 12
  • 56. 傳統命令式流程 import scala.collection.mutable.ArrayBuffer //抓出所有價格 val prices = new ArrayBuffer[Int] for(ticket <- tickets) { prices += getPrice(ticket) } //去除大於1000的 for(price <- prices){ if (!Less1000(price)) prices -= price } //跑迴圈找出最大值 var highestPrice = 0 for(price <- prices) { highestPrice = pickHighPriced(highestPrice, price) } highestPriceSunday, April 15, 12
  • 57. Functional Style val highestPrice = tickets.map{ x => getPrice(x) } .filter{ x => Less1000(x) } [“a”, “b”,”c”] .reduce{ (x,y) => pickHighPriced(x,y) }Sunday, April 15, 12
  • 58. Functional Style [1100, 900,800] val highestPrice = tickets.map{ x => getPrice(x) } .filter{ x => Less1000(x) } .reduce{ (x,y) => pickHighPriced(x,y) }Sunday, April 15, 12
  • 59. Functional Style val highestPrice = [900,800] tickets.map{ x => getPrice(x) } .filter{ x => Less1000(x) } .reduce{ (x,y) => pickHighPriced(x,y) }Sunday, April 15, 12
  • 60. Functional Style val highestPrice = tickets.map{ x => getPrice(x) } 900 .filter{ x => Less1000(x) } .reduce{ (x,y) => pickHighPriced(x,y) }Sunday, April 15, 12
  • 61. Scala 可以用 _ 簡化 val highestPrice = tickets.map{ getPrice(_) } .filter{ Less1000(_) } .reduce{ pickHighPriced(_,_) }Sunday, April 15, 12
  • 62. 繼續簡化 val higtestPrice = tickets.map.getPrice.filter.Less1000.reduce.pickHighPricedSunday, April 15, 12
  • 63. Scala 可以用空隔做 method call val higtestPrice = tickets map getPrice filter Less1000 reduce pickHighPricedSunday, April 15, 12
  • 64. State transformation 不需要 Mutate 變數就可以處理 CollectionSunday, April 15, 12
  • 65. 4. Function Composition 拆解和組合函式Sunday, April 15, 12
  • 66. Partial Functions 固定某些參數,回傳新的 FunctionSunday, April 15, 12
  • 67. Haskell add x y = x + y add 2 3 # 5 addTwo = add 2 addTwo 3 # 5Sunday, April 15, 12
  • 68. Scala 用⼀一個 _ wildcard def add(x:Int, y:Int) = x + y val addTwo = add(2, _:Int) addTwo(3) //5Sunday, April 15, 12
  • 69. Currying named after Haskell Curry • 將⼀一個有 N 個參數的 function 轉換成 N 個只有⼀一個參數的 function 的過程Sunday, April 15, 12
  • 70. Haskell 每個 function 都是 Curried,其實都只有⼀一個參數 max 4 5 # 其實是 (max 4) 5 # max函式的宣告是 max :: Ord a => a -> (a -> a) max 4 :: (Ord a, Num a) => a -> aSunday, April 15, 12
  • 71. Haskell 每個 function 都是 Curried,其實都只有⼀一個參數 max 4 5 (max 4) 先得到⼀一個 # 其實是 Partial Function,然後再 帶入5 (max 4) 5 # max函式的宣告是 max :: Ord a => a -> (a -> a) max 4 :: (Ord a, Num a) => a -> aSunday, April 15, 12
  • 72. Scala 可以將函式定義成 Curried 形式 def sum(a: Int, b: Int) = a + b sum(2, 3) // 5 def curriedSum(a: Int)(b: Int) = a * b curriedSum(2)(3) // 5 curriedSum(2) 先得到⼀一個 Partial Function,然後再帶入3Sunday, April 15, 12
  • 73. Compose 把兩個⼀一個參數的 Function 合併起來 可以串聯多個組合成新的函數 f(g(x)) = (f。g)(x)Sunday, April 15, 12
  • 74. Haskell 中間的串列省掉了 reverse( sort [2,5,1,10,5] ) # -- the . operator is used to compose functions reverseSort = reverse . sort reverseSort [2,5,1,10,5]Sunday, April 15, 12
  • 75. JavaScript 也可以自定 compose 方法 function compose(f, g) { return function(x) { return f(g(x)); } }Sunday, April 15, 12
  • 76. 5. Lazy evaluation • 直到真的需要才執行 • 增加效率,減少沒有用到的計算 • 可以簡潔地表達無窮 listSunday, April 15, 12
  • 77. Haskell is lazy functional language cycle [1,2,3] # [1,2,3, 1,2,3, 1,2,3, 1,2,3, 1,2,3, 1,2,3, 1,2,3,.... take 10 (cycle [1,2,3]) # [1,2,3,1,2,3,1,2,3,1]"Sunday, April 15, 12
  • 78. Scala’s lazy val useful for lazy initialization val a = { println("evaluating A"); "A" } // evaluating A // a: java.lang.String = A lazy val b = { println("evaluating B"); "B" } // b: java.lang.String = <lazy>Sunday, April 15, 12
  • 79. Scala Stream (lazy list) Scala 預設的 List 不是 Lazy def from(n: Int): Stream[Int] = Stream.cons( n, from(n+1) ) lazy val odds = from(0).filter(_ % 2 == 1) odds.take(10).print // 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, emptySunday, April 15, 12
  • 80. Ruby Enumerable::Lazy 容器不是 Lazy 的話,無窮元素就不能套 Combinator functions 了 require prime Prime.to_a # 這樣是無窮數列... Prime.take(10).to_a # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] Prime.lazy.select{ |x| x % 4 == 3 }.take(10).to_a # => [3, 7, 11, 19, 23, 31, 43, 47, 59, 67]Sunday, April 15, 12
  • 81. 6. Pattern Matching • 模式比對 • 比對兩個 expression 是否可以相同 • 用途 • 控制結構 • 指派變數Sunday, April 15, 12
  • 82. Erlang 用來比對然後指派變數 X. "1: variable X is unbound" X = 2. {X, Y} = {1, 2}. "exception error: no match of right hand side value {1,2}" {X, Y} = {2, 3}. {2,3} Y. "此時 Y 是 3"Sunday, April 15, 12
  • 83. Erlang 來試試 List [H|T] = [1,2,3,4,5]. H. "1" T. "[2,3,4,5]"Sunday, April 15, 12
  • 84. Erlang (cont.) 來試試 List [A,B,C|T] = [1,2,3,4,5]. A. "1" B. "2" C. "3" T. "[4,5]"Sunday, April 15, 12
  • 85. Scala 用 match 比對 import scala.util.Random val randomInt = new Random().nextInt(10) randomInt match { case 7 => println("lucky seven") case otherNumber => println("get " + otherNumber) }Sunday, April 15, 12
  • 86. Scala 可以比對 Type val items = List(1, "foo", 3.5) for (item <- items) { item match { case i: Int => println("got an Integer: " + i) case s: String => println("got a String: " + s) case f: Double => println("got a Double: " + f) case other => println("got others: " + other) } }Sunday, April 15, 12
  • 87. Scala 可以加上 Guard 條件 val t1 = ("A", "B") val t2 = ("C", "D") val t3 = ("E", "F") for( tuple <- List(t1, t2, t3) ) { tuple match { case (one, two) if one == "C" => println("得到開頭是C的tuple") case (one, two) => println("blah") } } // blah // Got tuple starting with C // blahSunday, April 15, 12
  • 88. 總結Sunday, April 15, 12
  • 89. Functional Style • 強調 immutability,避免 Function 的 Side-effects • 雖然還是無法完全避免 side-effects (例如IO), 但是 FP Style 降低了 Mutable State,增加程式 碼正確性和 Concurrency 能力。Sunday, April 15, 12
  • 90. 要怎麼開始FP? • Functional Programing 不同程度地混搭在各種程式語言之 中: • Java 也可以寫 FP Style • Scripting 語言(Ruby/Python/JavaScript) 也提供了很多 好 用的 FP 技巧 • 但要學到 Avoid Mutable State 和 No-side Effects Function 的精髓,還是得試試 Functional 程式語言 • 建議多看看不同的 FP 語言,Lisp, Haskell, Scala, F# 概念 相通但語法差蠻多的Sunday, April 15, 12
  • 91. 入門推薦書籍Sunday, April 15, 12
  • 92. Thanks. • 感謝 @godfat (Haskell強者) 、大貓和 @idryman (Lisp 強者) 的指教 • 如果你有在寫FP,歡迎來找我聊聊、交 換名片,也許有機會來辦⼀一個 FP user group 聚會 :-)Sunday, April 15, 12