函數式編程二 異常處理

2021-03-02 小小肥T
基礎

函數式編程一

在java中用函數式的方式去做事情,Happy Path確實很好玩,但是編程中最不好玩的就是異常的情況。

通常函數式都是流式,然而通常不希望數據在流的過程中出現異常。於是出現了這麼三種處理方式:

1.

F#中提出了Railway Oriented Programming[2] 特別有意思的一個想法。

2.

Try/Success/Failed 模式最早是在Twitter中提出的, 後被引入Scala的標準類庫中。

3.

Option/Some/None模式是另一種處理模式。

2和3 在Scala文章中有詳細的說明: FUNCTIONAL ERROR HANDLING IN SCALA[3]

強烈推薦去看一下,文章不長,也不枯燥。中間對比和2和3的優缺點,和使用場景。

了解了一番之後,回過頭看Java的Optional弱雞一個。

背景

1.

項目中用的是Java,然後處理數據是一批一批的處理,之後改成了函數式,並沒有大動結構,只是用函數式給串了起來。

2.

由於傳統面向對象思想的束縛,throw exception,在流中的操作原子中有出現。

3.

每個操作原子中可能會有副作用。

4。對業務代碼無感知

1.能夠給調用方返回數據

函數式編程最佳實「踩」踐「坑」指南

函數式編程通常有這麼一個原則:

所有在操作原子中主動拋異常的行為都是耍流氓。

標準的函數式異常處理模式有什麼問題:無法攜帶異常數據。只能攜帶異常。

Java中有人實現了標準Scala的Try[4]。

有興趣的可以看看,代碼也很簡單,一會就能看完。看完之後,自己手動實現java Optional就很輕鬆了。

Try的實現增強版

1.定義接口,用於承接數據

public interface IError<T> {    T toSystemError();}

1.定義Try

// 定義承載數據類型為IN, 並且要實現IERROR// 定義異常類型為ERRORpublic final class Try<IN extends IError<ERROR>, ERROR> {
// 流進來的數據 private final IN value; // 承載的異常數據 private final ERROR error; // 發生異常的現場 private final Throwable exception;
// success的構造函數 private Try(IN IN) { this(IN, null, null); }
// failed的構造函數 private Try(IN IN, ERROR error, Throwable exception) { this.value = IN; this.error = error; this.exception = exception; }

public boolean isSuccess() { return Objects.isNull(exception); }
public boolean isFailed() { return !this.isSuccess(); }
public IN get() { return this.value; }// 流中數據類型的轉化 public <R extends IError<ERROR>> Try<R, ERROR> map(Function<IN, R> mapper) { return getData(mapper); }// 流中數據類型轉化處理 private <R extends IError<ERROR>> Try<R, ERROR> getData(Function<IN, R> mapper) { if (this.isFailed()) { return new Try<>(null, error, exception); } if (Objects.isNull(value)) { return new Try<>(null); } try { final R newValue = mapper.apply(value); return new Try<>(newValue); } catch (Exception e) { return new Try<>(null, value.toSystemError(), e); } }// 類似於flatMap,但是又不是。 public <R> R transformTo(Function<IN, R> converter) { return converter.apply(value); }// 這裡參照CompletableFuture的做法,並且參照了NodeJs的做法(error first) public void whenComplete(BiConsumer<Throwable, ERROR> afterFailed, Consumer<IN> afterSuccess) { if (this.isSuccess()) { afterSuccess.accept(value); } else { afterFailed.accept(exception, error); } }
// 這裡是Try的構造。 public static <I extends IError<E>, R extends IError<E>, E> Try<R, E> apply(Supplier<R> supplier) { return new Try<>(supplier.get()); }
}

1.用法

class TryDemoTest {
class TestClass implements TryDemo.IError<String> {
private String id;
public TestClass(String id) { this.id = id; }
@Override public String toSystemError() { return id; } }
@Test void shouldExecNextStep() { final TestClass test = TryDemo.Try.apply(() -> new TestClass("start")) .map(testClass -> new TestClass("1")) .map(testClass -> new TestClass("2")) .get();
assertEquals("2", test.id); }
@Test void shouldNotExecNextStep() { Function<TestClass, TestClass> exception = testClass -> { throw new NullPointerException(); }; TryDemo.Try.apply(() -> new TestClass("start")) .map(testClass -> new TestClass("1")) .map(exception) .map(testClass -> new TestClass("3")) .whenComplete((e, data) -> { // log ERROR and get Error data assertTrue(true); assertEquals("1", data); }, successData -> { // handle success Logic assertFalse(true); }); }}

總結

這裡和原始的Try的不同在於原始的Try是一個抽象類,Success和Failed都是其具體的實現,沒法同時持有success和failed,導致它沒法持有異常數據。

References

[2] Railway Oriented Programming: https://fsharpforfunandprofit.com/rop/
[3] FUNCTIONAL ERROR HANDLING IN SCALA: https://docs.scala-lang.org/overviews/scala-book/functional-error-handling.html
[4] Try: https://github.com/lambdista/try

相關焦點

  • 高階函數與函數式編程
    根據程式語言理論,一等對象必須滿足以下條件:Python 函數同時滿足這幾個條件,因而也被稱為 一等函數 。高階函數 則是指那些以函數為參數,或者將函數作為結果返回的函數。對高階函數稍加利用,便能玩出很多花樣來。本節從一些典型的案例入手,講解 Python 函數高級用法。
  • 函數式編程中的副作用概念
    前言為了清楚起見,請記住,副作用不是必需的壞事,有時副作用是有用的(尤其是在函數式編程範式之外)。今天聊一聊函數式編程中的隔離思想,它所想隔離的就是「副作用」我們先從其他角度來聊一聊副作用這個概念。函數式編程中的副作用概念如果函數有副作用,我們將其稱為過程函數式編程是基於沒有副作用的這樣一個簡單的前提。在這種範例中,副作用是被排斥的。如果函數有副作用,我們將其稱為過程,或者命令式。因此函數沒有副作用。
  • 函數式編程聖經
    上帝看到約翰·麥卡錫發明了表處理語言 Lisp,卻只用來學術研究,很是傷心,就把 Lisp 解釋器的秘密告訴了他的學生史蒂芬·羅素,史蒂芬·羅素將eval函數在IBM 704機器上實現後,函數式編程的大門第一次向人類打開了。
  • 函數式編程,真香
    最開始接觸函數式編程的時候是在小米工作的時候,那個時候看老大以前寫的代碼各種 compose,然後一些 ramda 的一些工具函數,看著很吃力,然後極力吐槽函數式編程,現在回想起來,那個時候的自己真的是見識短淺,只想說,'真香'。
  • Java如何支持函數式編程?
    函數式編程並非一個很新的東西,早在50多年前就已經出現了。近幾年,函數式編程越來越被人關注,出現了很多新的函數式程式語言,比如Clojure、Scala、Erlang等。一些非函數式程式語言也加入了很多特性、語法、類庫來支持函數式編程,比如Java、Python、Ruby、JavaScript等。除此之外,Google Guava也有對函數式編程的增強功能。
  • 函數式編程
    ,我們會看到如下函數式編程的長相:函數式編程的三大特性:immutable data 不可變數據:像Clojure一樣,默認上變量是不可變的,如果你要改變變量,你需要把變量copy出去修改。函數式編程的幾個技術map & reduce :這個技術不用多說了,函數式編程最常見的技術就是對一個集合做Map和Reduce操作。這比起過程式的語言來說,在代碼上要更容易閱讀。
  • 【第1679其】函數式編程淺析
    如果我們合理的使用了函數式編程,我們的代碼主體可能就會變成一個個「獨立且擁有合適名稱的純函數」,若干「純函數的組合」,「集中處理的副作用」。想要了解整體代碼邏輯,只需去閱讀函數組合在語意上做了什麼事情,副作用做了什麼,想要了解具體某個純函數做了什麼時,可以單獨去查看其定義,而不用去關心其它的函數。
  • 大前端進擊之路(一):函數式編程
    函數式編程概念一、什麼是函數式編程函數式編程(Functional Programming, FP),是一種編程風格,也可以認為是一種思維模式,和面向過程、面向對象是並列的關係。函數式編程是對運算過程的抽象,函數指的並不是程序中的函數或者方法,而是數學中的函數映射關係,例如:y=cos(x),是y和x的關係。
  • Python異常處理
    解決方案首先我們要了解異常才能處理異常那我們來就說一說異常的定義:程序運行過程中出現的錯誤或遇到的意外情況其次是錯誤的類型分別有:語法錯誤、運行錯誤、邏輯錯誤而我們通常出錯的理由無非是這些:輸入錯誤、下標越界、類型錯誤、操作不當等等接著我們來看一看報錯的構成吧接下來我們的重點來了,在python異常處理會用到
  • 大數據入門:Scala函數式編程
    提到Scala,首先會提到的一個概念,就是函數式編程,這也是Scala語言區別與其他程式語言的典型特徵。Scala是一門多範式(multi-paradigm)的程式語言,設計初衷是要集成面向對象編程和函數式編程的各種特性。
  • Python(27)常用指引:函數式編程指引
    函數式編程指引本文檔提供恰當的 Python 函數式編程範例,在函數式編程簡單的介紹之後,將簡單介紹Python中關於函數式編程的特性如 iterator
  • java8的函數式編程解析
    其實在java8就已經有java的函數式編程寫法,只是難度較大,大家都習慣了對象式用法,但在其它語言中都有函數式的用法,如js,scala,函數式其實是抽象到極致的思想。什麼是函數式編程 函數式編程並不是Java新提出的概念,其與指令編程相比,強調函數的計算比指令的計算更重要;與過程化編程相比,其中函數的計算可以隨時調用。
  • 函數式編程,我心中的 C 位!
    最常見的三種範式分別是面向對象程序設計、命令式程序設計和函數式程序設計。這三種思想體系並無優劣之分,通常我們都需要選擇正確的工具來完成工作。大多數軟體工程師對於函數式編程的概念並不太熟悉。實際上,歷史上的第二個程式語言Lisp就屬於函數式範式。
  • Python中的函數式編程
    (英語:functional programming)或稱函數程序設計,又稱泛函編程,是一種編程範型,它將電腦運算視為數學上的函數計算,並且避免使用程序狀態以及易變對象。函數程式語言最重要的基礎是λ演算(lambda calculus)。而且λ演算的函數可以接受函數當作輸入(引數)和輸出(傳出值)。
  • 為什麼函數式編程在Java中很危險?
    在我的日常工作中,我身邊的開發者大多是畢業於CS編程頂級院校比如MIT、CMU以及Chicago,他們初次涉及的語言是Haskell、Scheme及Lisp。他們認為函數式編程是一種自然的、直觀的、美麗的且高效的編程樣式。
  • 詳解函數式編程之Monad
    何為 MonadMonad 概念最初來自於範疇論,後來引入進函數式程式語言,由函數式編程發揚光大,漸漸滲透到其他主流語言中。FP 中具有代表性的語言是 Haskell,其特點是函數是第一公民,延遲計算、引用透明 [1]。
  • Golang 函數式編程簡述
    什麼是 Functional Programming 首先我們需要研究一下什麼是高階函數編程?所謂的 Functional Programming,一般被譯作函數式編程(以 λ演算 為根基)。函數式編程,是指忽略(通常是不允許)可變數據(以避免它處可改變的數據引發的邊際效應),忽略程序執行狀態(不允許隱式的、隱藏的、不可見的狀態),通過函數作為入參,函數作為返回值的方式進行計算,通過不斷的推進(迭代、遞歸)這種計算,從而從輸入得到輸出的編程範式。在函數式編程範式中,沒有過程式編程所常見的概念:語句,過程控制(條件,循環等等)。
  • Kotlin函數式編程
    那麼在函數式編程中當然一切皆是函數。在Kotlin中函數式的地位和對象一樣高,你可以在方法中輸入函數,也可以返回函數。函數式編程FP特徵:函數式編程核心概念:函數是「一等公民」:是指函數與其他數據類型是一樣的,處於平等地位。函數可以作為其他函數的參數傳入,也可以作為其他函數的返回值返回。
  • 白話 Python 的函數式編程
    今天和大家聊聊 Python 的函數式編程特性。
  • 現代C++函數式編程
    ,展示了現代C++實現函數式編程的方法和技巧,同時也體現了現代C++的強大威力和無限可能。函數式編程是一種編程範式,它有下面的一些特徵:函數是一等公民,可以像數據一樣傳來傳去高階函數遞歸pipeline惰性求值柯裡化偏應用函數C++98/03中的函數對象,和C++11中的Lambda表達式、std::function和std::bind讓C++的函數式編程變得容易