Scala與Haskell的嚴謹優雅性比較
函式語言主要優點是秉承數學的嚴謹性與可推導性,該文比較了純函式語言Haskell在代數方程上與Scala語言的不同性,突出了Haskell純函式語言的特點。
上述代數效果並不是在每個號稱函式語言中都會有,其他語言使用這樣的方程式會產生副作用,無副作用是函式語言引以為傲的主要特點,因為其他語言不會像 Haskell那樣等式兩邊產生相同的順序效果,如[3,4,5,6],有的可能是[3,5,4,6],這樣就沒有代數方程的嚴謹性了。
讓我們使用混合式語言Scala實現看看:
使用串聯與map函式先後不同就會導致結果順序不同:
第一行中,兩個集合首先串聯,然後針對串聯結果使用map函式,結果是,首先列印出"!"和"?",然後是f函式對每個元素的結果,列印出4次"*";而在第二行,對xs集合每個元素使用f函式後,在同樣對ys採取同樣操作,列印的結果和前面順序就不同了,不是"!?",而是"!*","?"符號夾在4個"*"中間了。
這表明,語句的前後順序在Scala會導致不同的程式結果,這種語句很顯然沒有代數方程的特點和嚴謹性,這樣的語句是無法可推導的,經不起推敲的,有副作用的,不是可交換性的associative。
那麼Scala是不是沒有辦法解決呢?原文給出了Scala的複雜解決方案:
測試結果:
現在函式的順序是冪等的。
對比一看,Haskell的優雅與簡單一目瞭然,很多人懷疑Haskell在消除副作用以後會破壞數學的優雅性,很顯然這不是真的。
Haskell for all: Algebraic side effects
初中或小學數學中我們都學過方程式:
f * (xs + ys) = (f * xs) + (f * ys)
左邊的方程等同於右邊的方程,而在函式語言中也是秉承這種交換性的,假設我們做個符號替換:
1. 使用 Haskell的map函式替換數學的乘號。
2. 使用Haskell的 ++ 運算子替換數學的加號。
這樣上面的方程式就變成了Haskell的等式:
map f (xs ++ ys) = (map f xs) ++ (map f ys)
也就是說,將集合xs和ys串聯起來後,然後基於串聯後的集合使用名為f的map函式,其結果等同於:對xs和ys每個集合單獨使用名為f的map函式,然後串聯這兩個結果。
使用Haskell REPL執行效果如下:
>>> map (+ 1) ([2, 3] ++ [4, 5]) <p class="indent">[3,4,5,6] >>> (map (+ 1) [2, 3]) ++ (map (+ 1) [4, 5]) <p class="indent">[3,4,5,6] <p class="indent"> |
上述代數效果並不是在每個號稱函式語言中都會有,其他語言使用這樣的方程式會產生副作用,無副作用是函式語言引以為傲的主要特點,因為其他語言不會像 Haskell那樣等式兩邊產生相同的順序效果,如[3,4,5,6],有的可能是[3,5,4,6],這樣就沒有代數方程的嚴謹性了。
讓我們使用混合式語言Scala實現看看:
>>> def xs() = { print("!"); Seq(1, 2) } >>> def ys() = { print("?"); Seq(3, 4) } >>> def f(x : Int) = { print("*"); x + 1 } <p class="indent"> |
使用串聯與map函式先後不同就會導致結果順序不同:
>>> (xs() ++ ys()).map(f) !?****res0: Seq[Int] = List(2, 3, 4, 5) >>> (xs().map(f)) ++ (ys().map(f)) !**?**res1: Seq[Int] = List(2, 3, 4, 5) <p class="indent"> |
第一行中,兩個集合首先串聯,然後針對串聯結果使用map函式,結果是,首先列印出"!"和"?",然後是f函式對每個元素的結果,列印出4次"*";而在第二行,對xs集合每個元素使用f函式後,在同樣對ys採取同樣操作,列印的結果和前面順序就不同了,不是"!?",而是"!*","?"符號夾在4個"*"中間了。
這表明,語句的前後順序在Scala會導致不同的程式結果,這種語句很顯然沒有代數方程的特點和嚴謹性,這樣的語句是無法可推導的,經不起推敲的,有副作用的,不是可交換性的associative。
那麼Scala是不是沒有辦法解決呢?原文給出了Scala的複雜解決方案:
-- f * (xs + ys) = (f * xs) + (f * ys) f =<< (xs <|> ys) = (f =<< xs) <|> (f =<< ys) <p class="indent"> |
測試結果:
>>> import Control.Applicative >>> import Pipes >>> let xs = do { lift (putChar '!'); return 1 <|> return 2 } >>> let ys = do { lift (putChar '?'); return 3 <|> return 4 } >>> let f x = do { lift (putChar '*'); return (x + 1) } >>> runListT (f =<< (xs <|> ys)) -- Note: `runListT` discards the result !**?**>>> runListT ((f =<< xs) <|> (f =<< ys)) !**?**>>> <p class="indent"> |
現在函式的順序是冪等的。
對比一看,Haskell的優雅與簡單一目瞭然,很多人懷疑Haskell在消除副作用以後會破壞數學的優雅性,很顯然這不是真的。
相關文章
- 比較優雅的後臺條件查詢
- Apache與Nginx的優缺點比較ApacheNginx
- PHP介面與性狀的優雅應用PHP
- Java,Pyhon,Scala比較(一)map,reduceJava
- Java,Python,Scala比較(三)wordcountJavaPython
- 從OOP和FP看蘋果Swift語言與Scala比較OOP蘋果Swift
- Apache與Nginx優缺點比較ApacheNginx
- IPSec ×××與SSL ×××優劣比較
- Apache與Nginx的優缺點、效能比較,到底選擇哪個比較好?ApacheNginx
- css與 js動畫 優缺點比較CSSJS動畫
- Oracle vs PostgreSQL Develop(25) - plsql vs plpgsql(語法嚴謹性)OracleSQLdev
- 從5.56*45mm NATO彈看塔科夫的資料嚴謹性
- codeigniter3整合composer管理工具,比較優雅的做法
- 書寫一個嚴謹的單例單例
- 嚴謹而浪漫!20個來自德國最優秀的網頁設計網頁
- 優先佇列的比較器佇列
- 「SAP技術」SAP不夠嚴謹?
- 比較Java與Node.js的併發性和效能- maxantJavaNode.js
- Solr與Elasticsearch的優缺點比較總結和歸納SolrElasticsearch
- 全域性索引和本地索引的比較索引
- PostgreSQL與MySQL的比較 - hackrMySql
- MVVM與MVC模式的比較MVVMMVC模式
- XTask與RxJava的使用比較RxJava
- JavaScript 與 Java、PHP 的比較JavaScriptPHP
- Hadoop與Spark的比較HadoopSpark
- CMM/CMMI 與敏捷的比較敏捷
- Hibernate與 MyBatis的比較MyBatis
- CoffeeScript與Ruby的比較
- 理解力尚可,但嚴謹度欠缺……
- Vue與React比較VueReact
- 【Redis與Memcached比較】Redis
- RecyclerView與ListView比較View
- js與jq比較JS
- PostgreSQL與MySQL比較MySql
- Vuex與Redux比較VueRedux
- Go 與 C++ 的對比和比較GoC++
- 那些優雅靈性的JS程式碼片段JS
- 嚴謹的防禦DDoS思路幫助你實現有效快速、高準確性的應對DDoS