2016-11-17 64 views
4

(我想引用另外一個問題作爲參考:How do I elegantly check many conditions in Erlang?二郎:成功案例和錯誤處理事務

的「從錯誤處理是分開的成功案例代碼」通用的形式似乎是:

try 
    ok = do_x(), 
    ... 
    ok = do_y() 
catch 
    error:{badmatch, x_failure} -> do_something1(); 
    ... 
    error:{badmatch, y_failure} -> do_something2(); 

當try子句中的函數執行某些副作用時,如寫入文件,發送網絡數據包,將數據寫入數據庫等,如何使用此模式?在catch子句中是否有一個「回滾」的通用模式?例如:

try 
    %How to make this whole block a transaction? 
    ok = step_1_write_file(), 
    ok = step_2_write_database(), 
    ok = step_3_send_packet(), 
    ... 
catch 
    error:{badmatch, database_failure} -> clean_up_step_1() %delete file? 
    error:{badmatch, sendpacket_failure} -> clean_up_step_1_and_2() %?? 

這似乎是錯誤處理變得繁重,其中需要被執行的清理是依賴於try塊失敗的步驟。

有沒有把這個作爲一個交易一般的編程模式,而在try塊的成功步驟失敗從句前是``解開「?

+3

成功_typing_在這裏似乎並不相關。 –

+0

@AlexeyRomanov問題標題編輯 – Tommy

回答

3

我個人學會了這樣的算法程序通過傳遞「驗證」,並optionaly,「終結」的一些通用的迭代函數列表

所以,你的情況可以編程這樣的:

noop() -> ok. 

transaction([{Fun, Rollback} | Rest]) -> 
    try 
     {ok, Result} = Fun(), 
     [Result | transaction(Rest)] 
    catch Type:Reason -> 
     Rollback(), 
     erlang:raise(Type, Reason, erlang:get_stacktrace()) 
    end; 
transaction([Fun | Rest]) -> 
    % not every action require cleanup on error 
    transaction([{Fun, fun noop/0} | Rest]); 
transaction([]) -> []. 


main() -> 
    Actions = [ 
     {fun write_file/0, fun cleanup_file/0}, 
     {fun write_database/0, fun cleanup_database/0}, 
     fun do_safe_thing/0, 
     {fun send_packet/0, fun cancel_send_packet/0}, 
    ], 
    transaction(Actions). 

正如你所看到的,因爲這個名單使用身體評估遞歸,iter通過這個列表將形成一堆調用,並且如果在某些步驟中這些功能中的一個將會失效,則堆棧將被展開並且每個清理功能將以相反的順序被調用。

例如,如果do_safe_ting/0將下降,將按此順序調用清理功能noop/0,cleanup_database/0cleanup_file/0

當然,這可以通過不同的方式進行編程,而不是通過重新拋出異常,而是通過例如返回{ok, Result}{error, Reason}。這只是實施細節。

+0

在我的情況下,使用你的例子,如果'send_packet'失敗,我想回滾*前面的所有步驟,而不僅僅是前面的步驟。我想我可以傳遞所有回滾函數的列表並繼續追加到列表中。 – Tommy

+0

@Tommy Actualy,每個前面的回滾函數都會被調用,因爲erlang:raise會被stack – seriyPS