是否Future/Promise模式 能實現的FRP都能更好的實現?

如題。

另外,C#中的Task相關的編程模型算是什麼(更專業的術語)?

原諒我無知


我是FRP粉

---------------------- 2016-01-16 的回答----------------------------------

frp表現出能夠「消除回調」的能力。但是我用frp來設計系統的時候和我用future來解決問題的時候完全不是一種思維方式。也不覺得在所有場景下FRP的實現都「更好」

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

也許隨著對FRP理解和應用的深入,我會改變答案,或許FRP確實都能實現的更好,從目前的經驗(scala widok, elm-lang,scala future/promise)還不足以讓我認為FRP「都更好」


取決於你對Future/Promise模式和FRP的定義。

我自己做過一套 Future/Promise 模式(Stateless Future),提供了非同步計算功能。我也做過一套 FRP 框架 (Binding.scala),提供了數據綁定功能。

然而,我至今還不知道 Future/Promise 模式和 FRP 的精確定義是什麼。我覺得二者的概念上有重複的部分。

但我明確知道,數據綁定和非同步計算肯定不同,這就是我取名 Binding.scala 不叫 React.scala 的原因。

如果硬要說有什麼東西可以統一 Future/Promise 模式和 FRP ,我想應該是計算表達式,即 Monad 。所以無論是 Binding.scala 還是 Stateless Future 都可以看成 Monad 。

問題在於,Monad 的基本定義是 point 和 bind 兩個函數。這兩個函數過於底層,上層使用很不方便。直接用 Monad 寫代碼就跟手寫彙編一樣吃力。有沒有辦法像編寫普通代碼一樣使用 Monad ,同時又具備各種數據綁定、非同步計算等黑科技功能呢?

我給出的方案是 Each ,用戶只需要編寫普通命令式語法,就能自動被 Each 轉換成 monadic 風格,無論是 Future 還是 FRP 都不在話下。

最後, @vczh 的編程水平比我預想的還要低啊。C# 的 task 怎麼可能是 comonad ? comonad 要求必須有對表達式立即求值的功能,會導致計算表達式必須是個簡單的值,就喪失了實現各種黑科技的可能了。


C#的task叫comonad,但是本質上也是Future/Promise,只是它通過comonad的手段把promise給藏起來了。


我是Scala粉

其實題主問的問題很有價值,學習本身就是將一個個離散的點(概念)聯繫起來的過程,問出這樣的問題並不奇怪,希望能解答題主的疑惑。

首先還是得寫點科普的東西

Scala的Future是個Monad(twitter的Future也是),抽象為Monad有2個好處(我能想到的):

1. 通過Monad的語義(dependent computation)將多個非同步操作串聯起來,這樣可以有效避免/隱藏回調(onSuccess/onComplete/onFailure...),其次就是隱藏Promise(Scala實現了完整的FuturePromiseCallback模型)

2. 可以與其它的Monad(如List, Option)更好的組合(Monad Transformers), 來實現更好的monadic programming,具體的對一個Web應用來說HTTP Request取出的參數無非就是List或Option,對於後端RPC/資料庫請求返回的數據無非也是List/Option

(對於輪子哥指出的C# Task是個comonad,我還是不理解,為什麼要對Tasks做coKleisli composition 對下游計算暴露all substructures的好處是啥?世界之大,希望有人來科普。)

Scala的Future雖然沒有提供Applicative的實現,但是有sequence組合子,可實現future的橫向組合

Scala的Future其語義是代表強非同步計算,也就是每次flatMap組合的future都會被提交到ExecutionContext(默認是forkjoinpool)中,如果希望下游的計算都在同一個線程內完成,可以使用可控性更高的scalaz.concurrent.Task,它需要顯式調用Task.fork才會被提交到池(當然你也可以實現single thread的ExecutionContext)。

(scalaz.concurrent.Task內部實現了trampoline,可控性非常高,可以做Explicit Continuation(自己造的詞),關於這個我還沒仔細研究,有坑回來再填)

js的Promise 也是一個Monad(直覺告訴我)

FRP 是一種編程範式,FRP system是對signals的 processors,其first class values是time-varying values (每時每刻都在變化的值),不敢多寫了,怕被邵成大大教育,希望題主也區分一下FRP和RP(否則也會被教育的)。

除了Haskell的FRP庫以及一些語言支持FRP外,其實我們大部分接觸的是RP。

RP 可簡單的視為面向事件流的編程的範式,沒有FRP那麼嚴格(還要求behaviors具有明確的指稱語義),下面我也只討論RP

對於形如 a := b + c 指 a的值會隨著b,c不斷變化而變化,如何傳遞變化?那就是把b,c視為被觀察者(Observable),a視為觀察者(Observer),觀察者a收集導致被觀察者b, c的發生改變的事件,a可通過重演事件流確定當前時刻的值,這個事件流的獲取可以是poll/push,同步/非同步,而first class values是event stream

一些框架(如Rx*)中的Observable也是一個Monad, 使得我們可以對Observable做combination

科普到此就行了,下面來回答問題

是否Future/Promise模式 能實現的FRP都能更好的實現?

引用Rxjs book中的一句話:

Observable is Promise++. A Promise is simply an Observable with one single emitted value. Rx streams go beyond promises by allowing many returned values.

其實這句話已經很好的回答了你的問題,首先解釋Observable is Promise++,從範式和模式的角度來說,Promise去實現RP當然是可行的,而且Promise就是單值(單個離散的事件)的Observable,說白了就是一個一次性的,一個是一直存在的,對於Future也是一樣的,剝離其Monad的語義,Future就是代表這單次的非同步計算,對於下游的計算它就是個Observable

我想它們的關係和區別也已經顯現出來了,一個計算是單次的,一個計算是延續的,後者概念上涵蓋前者,前者是模式,後者是範式,模式可以一定程度上支持範式

這也解釋了為啥Finagle (Twitter 的非同步RPC)是響應式的,每個RPC調用都返回一個Twitter的Future,對於RPC的語境(場景),必定是單次計算(request -&> response),Twitter的Future可視為後續計算的Observable

下面討論所謂的「更好」,其實已經沒有必要討論了,因為他們不是對立關係,但是我想說的,所謂的「更好」應該是針對需求方,需求方的使用場景,對於GUI肯定用RP爽,對於簡單的單次非同步計算不需要非用RP

最後說一句,FRP/RP is the future

謝邀

(本身對這個問題感興趣,最近太忙,拖了好久才答,我不是大神,我只是剛好手頭上的項目是Reactive的,我司也在大量使用Future編程)


Rx系列重度用戶,Promise有幸用過一點來說說。Promise能做的用Rx都能做,Promise相當於Rx體系下的一個子集(類似flatMap),完整的Rx體系不能做的遠比Promise多。推薦樓主看下Rxjs,Rxjava,Rxswift之類的庫(跟FRP相關的應用標準庫)。


可以看一下這個: SHOULD use appropriate abstractions only where suitable - Future, Actors, Rx


推薦閱讀:

如何將MATLAB轉化為C#?
如何看待2017的 The .NET Language Strategy 去掉了C++/CLI支持?
什麼情況下使用異常處理?
C# 中如何有效地釋放內存?
如何評價微軟推出的 .NET Native?

TAG:C# | 編程範式 | 反應式編程ReactiveProgramming | Promise |