顯示具有 functional programming 標籤的文章。 顯示所有文章
顯示具有 functional programming 標籤的文章。 顯示所有文章

2016年1月29日 星期五

淺談函數式編程和 React

函數是有定義介面的運算單元。

介面有用的地方是抽象化,你只需要知道輸入什麼(Input)、會得到什麼輸出(Output),你不用知道它細節是怎麼做到的。其實有個超能力在背後,也沒關係。當然這是在沒有碰到 bug 和有良好文件、不需要修改它的前提之下。

再強調一次,你只需要理解輸入(input)會對應到什麼輸出(output)就夠了。


問題來了,怎樣的輸入和輸出的對應「介面」會容易讓人理解?

1. 有意義的函數名稱 2. 多寫沒有副作用的純函數 1. 有可預測性: 每次輸入得到相同的輸出、函數內部不存狀態 2. 沒有副作用: 運算過程中不會改到外界的變數,像是不改傳進來的參數、不改可以存取的全域變數。 3. 顯式(Explict): 函數和外界溝通的管道只有,傳進來的參數和回傳值。 3. 簡化參數、對資料結構的 Information Architecture 做良好設計 4. 用函數來定義函數 1. 柯里化(Currying): 透過不同給參數來產生新的函數 2. 合成(Compose): 透過 pipeline 串接函數的input和output、隱藏參數,產生新的函數。

函數式編程為什麼強大、有彈性?

1. 把每個函數切得很小,容易更新、維護、平行處理、多人共同開發、被理解 2. 透過合成(Compose)把小函數變成大函數,比用繼承有彈性的多 3. 對集合實做 Functor 介面,讓一般函數都可以對集合操作。(array, matrix, tensor) 4. 把純和不純的函數分開來管理,容易找到問題點。

函數式編程為什麼難寫?

因為函數式編程想要把程式變簡單。但大家都知道「變複雜是簡單的、變簡單是複雜的」。所以這種方式寫程式需要設計、思考,你會是一個程式「設計師」。但如果你現在的專案寫完之後沒人會看、不會再改、不用維護、規模不大,也許函數式編程並不能幫到這個專案多少。

寫 React 就是實踐函數式編程

1. 透過自定義元件,定義畫 View 的抽象化函數樹 2. 透過 JSX 語法把小函數組合成大的函數 (等同於 compose) 3. 鼓勵大家寫 dumb 元件、也就是純函數 4. 用唯讀的 props,限制大家不能改傳進來的參數、減少副作用 5. 透過導入 flux,把純和不純的函數分開來管理 1. 純的 (對資料只做讀的動作):每個元件中的 render function 2. 不純的 (對資料做讀和寫的動作): flux中對 action 的 callback、redux 中的 reducer、父元件傳給子元件的 callback 3. glue code:元件中其他的 javascript

從函數式編程的角度看 React 的問題?

1. 沒有什麼高階的集合操作方法: 1. 把 lodash.js 集合操作拉進來用? 2. 用 lenses 的方式來穿透深長不露的 states? 2. 沒有簡單的 currying 語法 1. 像是傳不同參數給 html5 input 元件,生出各式各樣對數字的、email、submit、text的自定義元件。最簡單寫法應該是 2. export function NumberInput(props) { 角括弧input type=“number” {…props} /> } 3. PS: function 一定要有名字不然 debug 會很慘。 3. 雖然集中管理了不純的函數,但還是很難寫:現在透過修改 store 中的 state 來控制元件,這邊 action 觸發的都是不純的函數,如果元件架構一深,還是會很難改。有解嗎?這邊還沒找到什麼 coding guideline… 1. 多個 container smart 元件 2. flux 的多個 store 3. redux 的階層式 reducers ----------- 延伸閱讀: deku: functional alternative to react how to use classes and sleep at night (in a functional way)

2016年1月28日 星期四

Hey Underscore, You're doing it Wrong! (介紹函數編程)

----------------------------------------------------------
Curried Function:到拿到所有需要的參數前... 一直回傳新函數的函數。
var add = function(x) {
  return function(y) {
    return x + y;
  }
}

var add3 = add(3);
add3(4); //return 7

add(3)(4); //weird thing 
autoCurry in Wu.js will save us
var add = function(x, y) {
  return x + y;
}.autoCurry();

var add3 = add(3);
add3(4) //7

add(3,5) //8 => not weird any more!!!
但我們為什麼需要 curry?參考下面這個組成新 function getTheOdds 的例子。 有了currying,我們可以透過給予不同參數來建立新的函數。
var filter = function(f, xs) {
  return xs.filter(f);
}

filter(isOdd, [1,2,3,4,5]) // [1,3,5]

var getTheOdds = filter(isOdd);
getTheOdds([1,2,3,4,5]) //[1,3,5]
再來一個用loadash的酷例子
//沒用currying、不函數化的寫法
var firstTwoLetters = function(words){
  return _.map(words, function(word){
    return _.first(word, 2);
  });
}

//函數化的寫法(如果underscore吃參數的方式是反過來的話)
var firstTwoLetters = _.map(_.first(2));

//更函數化的寫法
_.map(_.first(2), ['jim', 'kate']) //['ji', 'ka'] 
=> Underscore.js的參數排列法讓currying變得不可能 總結currying的優點有下面四個: 1. 一般化函數、要傳的變數名消失了 2. 透過給不同參數就可以生成不同的函數 3. 更簡潔的定義 4. 讓函式的組合/合成 (composition) 變的可能 ---------------------------------------------------------- 組合/合成 (composition):用多個函數來組成新函數 簡單的例子,用 first() 和 reverse() 來合成 last 函數
var last = function(xs) {
  var sx = reverse(xs);
  return first(sx);
}

var last = compose(first, reverse);

last([1,2,3]) //3
另一個例子,chain backwardly
var wordCount = function(str){
  var words = split(' ', str);
  return length(words);
}

var wordCount = compose(length, split(' '));
wordCount("There is a way to save the world") //8
Category Theory: 多個函數組合(compose),作用域互相對應的理論。Connecting the dot. 總結組合: 1. 能從其他函數組成新函數 2. 組合過程中把參數藏起來 3. 極為高階的寫程式 4. 有數學理論在後面支持 ------------------------------------------------------------------ Functors map 打開了後面的 object 然後做一些事、再放回 object
var plus1 = function(x){ return x + 1 }

plus1([3]) //wrong!!

map(plus1, [3]) //4
剛剛舉的例子,map 只能操作 array object、但下面試圖用 map 操作所有 object
map(plus1, MyObject(3)) //MyObject(4)

MyObject = function(val) {
  this.val = val;
}

MyObject.prototype.map = function(f) {
  return MyObject(f(this.val));
}
如果對 object 定義了 map function,它就變成 functor null check的例子、Dynamic Safety:
map(plus1, Maybe(3)) //=> Maybe(4)

map(plus1, Maybe(null)) //=> Maybe(null)

Maybe = function(val) {
  this.val = val;
}

Maybe.prototype.map = function(f){
  return this.val ? Maybe(f(this.val)) : Maybe(null);
}
把 ES6 promise 變 functor 的例子
map(populateTable, $.ajax.get('/posts');

Promise.prototype.map = function(f) {
  var promise = new Promise();
  this.then(function(response){
    promise.resolve(f(response));
  });
  return promise;
}
再來一個和 html 合作的例子:對有和沒有 user_login 的情況下,更新歡迎頁面。
$div = $("#myDiv");

//dot 會把 user.name 拿出來
var getGreeting = compose(concat('Welcome '), dot('name'));

var updateGreetingHtml = compose($div.html, getGreeting);

map(updateGreetingHtml, Maybe(App.current_user));
underscore 不讓人 extend map 總結 functor 能: 1. 改變函數的行為卻不用變動 open/closed principle 2. 不光只有 map, 還有 reduce & compose 3. 直覺且非私人的 api 4. free formulas 5. 動態型別安全/檢查 ------------------------------------------------------------- 總結:underscore 能變得更加 functional。希望有更 functional 的 library

2015年11月20日 星期五

程式設計法與人生 (Programming Paradigm and Life)

解決問題流行的方式有兩種。
  1. 想出第一步要做什麼,然後開始做、做完再想下一個。
  2. 是把大問題切成數個小問題一直切到夠小,然後再一個一個做。
第一個 叫做命令式程式設計、第二個 叫做宣告式程式設計。
第一個 想要回答「怎麼做」、第二個 想要回答「做什麼」。
第一個 是工程師做的事、第二個是設計師做的事。
第一個 叫做敏捷式開發流程、第二個 叫做瀑布式開發流程。
第一個 叫做 connecting the dots、第二個叫做 設計研究。
第一個 叫貪婪演算法、第二個叫分治演算法。
第一個 叫活在當下、第二個叫有遠見。
第一個 把問題序列化、第二個把問題 心智圖化。

用第一種的人想出了外掛 - 設計模式 (design pattern)
用第二種的人想出了外掛 - 狀態機和不變資料 (state machine + immutable data)

還記得大二演算法排序的作業,標準解法是先做第二個 (qsort)、然後問題變小了就接第一個 (shell sort)

各有各的使用時機,冷靜分析後我從函數式編程的信仰者回到無神論者了。
第一個適合許多未知、經常變動的問題。
第二個適合有固定答案、行為可預測的問題。

嗯嗯 不過當然用 react 還是比其他好 XD
---
目標努力 iterative 的 functional programming~ 
=> 每次用第一個方法切一小塊問題,用第二個方法解。

2015年11月9日 星期一

函數式編程介紹 ( 1 / 2 )

函數式編程的專有名詞篇


純函數 ( Pure Function )給同樣輸入參數,就會回傳同樣結果的函數,而且沒有任何可觀察到的副作用。Pure Function 的例子:sin(x)。非 Pure Function 的例子:getChar()、random()、還有許多類別中的 member function。從數學上來理解,純函數就是一個數學函數,一個輸入會對到一個輸出。

純函數的特點:
  1. Portable / Self-Documenting :完全是自給自足的 ( self contained ),沒有外在的依賴 (Dependency)。
  2. 可暫存 ( Cacheable ):把計算值暫存的技巧被稱作 memorization,可以用來避免重複計算,加速程式。
  3. 好測試 ( Testable ):不需要管上下文和呼叫順序就可以測試。
  4. 適合平行處理、純函數是線程安全的 ( thread safe )。
  5. 引用透明 ( Referential Transparent ):一個引用透明的運算式指的是 如果這個運算式可以被他的值 (回傳值) 替換而不影響整個程式的行為。簡單講就是有可交換性啦。
  6. 可以熱抽換 ( Hot-Loading ):因為不依賴外部的狀態。

副作用 ( Side Effect )如果一個函數或運算式 ( expression ) 被說有副作用,這指的是它改變了一些狀態 ( states ) 或是 跟呼叫他的函數或外在世界,有可觀察到的互動。舉例來說,一個函數可能會改變全域變數或函數的靜態變數、改變傳進來的參數、引發例外 ( exception )、列印資料到螢幕或是呼叫了其他有副作用的函數。如果有了副作用,函數的行為會受到歷史、執行順序的影響。這樣子一來,想要理解或除錯有副作用的函式會較難,因為就必須要了解他的上下文 ( Context ) 和執行歷史。

顯式 ( Explicit ):形容函數与外界交換資料只有一個唯一管道——参數和回傳值。和顯式的相反是隱式 ( Implicit )。

一級函數( First-Class Function ):指的是語言支援把 Function 當成第一類公民,可以支援一般變數的操作,像是把 Function 當成變數傳給另外一個 Function。

Lambda Function:Lambda 運算式是匿名函式,可用來建立委派或運算式樹狀架構類型。

形式系統 ( Formal System ):形式系統可以的廣泛地被定義為任何基於數學模型的、良好的抽象思考的系統 ( system of abstract thought )。

Currying:這個技巧能把接受多參數的函數轉換為多個連續呼叫的單一參數函數。

閉鎖 ( Closure ) / MDN:Closure 是可以使用獨立 / 自由變數的函數。換句話說,Closure 記得它實體化時的環境變數。

PS:專有名詞那邊引用了很多別人的解釋,可以點前面的連結進去看完整版。

-----------------------------------------------------------------------------------------


函數式編程簡介篇

函數式編程的的哲學就是假設副作用 ( side effect ) 是不正確行為的主要原因。所以努力想要控制和管理副作用,經常的解法就是把純函數、單子和不純的函數 ( impure function ) 分開來管理。另外鼓勵大家多寫純函數。我們對待資料要像玩戲法般,一直傳來傳去、禁止使用狀態 ( state ) 和副作用。剛剛這段文字有提到很多專有名詞,但這樣子怎麼寫程式?這邊我開始介紹一個新工具叫 柯里化 ( currying )。

Currying:
Currying 把任何的函數轉換成 一連串只做單一事情的函數,各個擊破。

Curring的概念是簡單的。它讓你呼叫函數 A 時可以傳比預期還少的參數。然後這個函數 A 會回傳函數 B函數 B 需要的參數是先前沒傳進函數 A 的參數。所以整個流程是:

函數A (x, y) 可以等價於下面這段
-------------------------------------
函數B = 函數A (x)
函數B (y)

先前傳進去函數 A 的參數會利用閉鎖 ( Closure ) 的方式變成函數 B 的環境變數。看完上面這段解說,一定會想這什麼鬼東西?來看看 這邊的例子 會容易理解的多。利用這個技巧,就可以選擇一次傳所有參數或是把參數分幾次傳給數個函數。

lodash提供了把函數 curry化的工具,用法:

var divide =           function (x,y) { return x / y } 可以被改寫成
-----------------------------------------------------
var divide = curry( function (x,y) { return x / y } );

之後就可以被這樣呼叫

divide ( x ) ( y );

或是

var xDividedBy = divide ( x );
tenDividedBy ( y );

PS:這工具的名字是為了紀念一個美國數學家 --- Haskell Curry,跟咖哩沒有關係。

接下來我們看另外一個工具 代碼組成( compose )。

組合 / 組成 / 合成 ( compose )
Composition 把許多函數用管子 ( pipe ) 連接起來,變成一個新個函數。


f, g 都是函數,x 在它們之間被傳遞。
注意g函數的參數是 x、f函數的參數是 g函數的回傳值。
較常用在 f & g 都吃同一類參數的時候 (ex: 字串)。


例子:變大寫和去空白函數 = compose (變大寫函數, 去空白函數)

因為有了一級函數、Currying、組合,Pointfree 風格變得流行了起來,指的是函數呼叫時不用提到它要處理的資料。例如:

var snakeCase = function ( word ) {
      return word.toUpperCase( ).replace( /\s+/ig, '_' );


可被改寫成
-----------------------------------------------
var snakeCase = compose ( replace( /\s+/ig'_' ), toLowerCase );

用組合形成新函數就不用提到資料 --- 也就是之前寫法中的 word

Pointfree的編程風格可以讓我們移除不需要的名字 (names),讓我們保持簡潔 ( concise ) 和一般化 / 泛型 ( generic )。但要小心 Pointfree 是個雙面刃,有時會把原來的目的變得模糊。

( 以上是書本前五章的內容,其他待續... 後面有點硬,不知道什麼時候會看。)

---
這篇文介紹得很好 函數式編程

2015年11月8日 星期日

Javascript 中的函數式編程 (a talk @ ReactiveConf 2015)

影片:Functional Programming in Javascript By Daniel Steigerwald 

Daniel 是 google 前員工、也是 https://github.com/este/este 這個 React + Redux + immutable.js Starter Kit 的作者,在 Github 上有 1800 顆星。

我認為函數式編程 ( Functional Programming ) 將在明年成為主流。像在 C++11 和 Java 8 中已經開始有 lambda function。我接下來聊我已經用在 Production 的東西。我認為函數式編程已經在前端的世界已經被 React 引入。

什麼是函數式編程?
沒有什麼神秘的東西,到處都是純函式 ( Pure functions everywhere )、不可變的數值 ( Immutable values )、用組成而不用繼承 ( composition over inheritance )、紀錄代替類別 (records over classes)、處理好副作用 ( taming side-effects )。我覺得 functional programming 有點像是人工智慧,聽起來有點酷、有點奇怪,但當你了解了它以後,就只是無聊、很平常的寫程式而已。

為什麼要函數化 ( functional )? 
因為軟體正在吃掉這個世界,它必須要做到最好。函數式編程已經被證明,比較少臭蟲 ( less bugs)、較不複雜 ( less complexity )、程式碼更可讀 ( more readable code ) 和更好的效能 ( more performance )。

物件導向程式設計 ( OOP ) 有什麼問題?
沒有問題,只是很難、常被誤用,而且有時是難以避免的。整個物件導向程式設計的模式 (paradigm) 是基於「送訊息給物件是唯一跟狀態 ( state ) 互動的方法」,因此狀態就會被分散。於是在分散式系統中狀態的一致的難度是跟世界和平一樣的 XD


物件導向程式設計 和 函數式編程的設計模式比較
Scott Wlaschin是很好的講者,不像我鼓勵大家去聽他的演講。

在 OOP 中,每次方法在某個實體上被呼叫其實就是副作用。副作用很難被追蹤、被理解,沒有人喜歡驚喜。驚喜在生活中是好的,但驚喜在程式碼裡面沒有任何好的地方。我們被教導要用繼承,但他是陷阱,程式碼會不夠彈性、很難之後改變。設計模式最難的是如何幫這些模式取名字,動詞偽裝成名詞。策略、工廠、Commands...

React 中最耀眼的原則是函式組成法 ( function composition )。

純函數 ( pure function ) vs 髒類別 (dirty class)

純函數沒有任何副作用。他很難去違反只負責一件事的原則 ( single responsibility principle) 因為純函數只有一個明顯的目的 --- 把輸入轉成輸出。所以測試就變得超簡單。

類別是髒的。互叫一個簡單的類別函式,就會改變它。誰做的?為什麼做?我們永遠不知道 ( 直到我們 debug 後)...

推薦這本 github 上的書適當的函數式編成指南 ( 在 github 上有 6000 顆星)


如果你不理解這個程式,沒關係。我也不懂。在函數式編程裡它等於 ( (4 + 0) * 2) + (4 * 2)
Immutable.js
不可變資料一旦被建立就不能被改變,這使得更簡單的應用程式開發,不用預防性的回傳複製品,更可以使用間單的邏輯來達到進階的 memoization 和改變偵測。一個針對不變資料的可變 API 並不改變原來資料,而是總是產生新的資料。
   - 和 原生的 Javascript array 有很相似的 API
   - 保持永遠的不可變 (List, stack, map, orderedMap, Set, OrderedSet and Record)
   - 非常快

---
PS:作者講的不多,上面很多都是照投影片上大量文字打的。

2015年11月4日 星期三

Be predictable, not correct. by Pete Hunt (from Instagram)


Pete Hunt from Instagram Web Team.

What makes UI hard?

最難的是管理所有使用者的狀態 ( state ),更可怕的是隨時間改變的狀態。然後 Unit Test 不能完全測試 UI 的所有情況 ( 太多例外 )、靜態分析 ( JSLint / JSHint ) 也不行。因為這樣的複雜度,我不曾試著讓 UI 完全正確。我只是試著讓 UI 變得可預測。

Two silver bullet:

1. Composition: 讓簡單的 function 組合成更大的 function。
2. Idempotence: 讓每次同樣的 Input 得到同樣的 Output。不變性 ( Immutability ) 的資料結構讓我免費得到 Idempotence。努力讓 mutable state 的數量越少越少,mutable state只有一個owner。

React.js 讓工程師照著上面的兩個規則。
React 是 宣告式的 ( declarative ) JQuery。

React

 > Data 輸入 => virtual DOM 輸出。
 > 當資料改變的時候,就整個重繪,所以少掉很多 State。

Demo Example (從15- 分)

Demo on jsfiddle.net jsbin.com
一直在看Code