首頁 >後端開發 >php教程 >PHP:單子

PHP:單子

DDD
DDD原創
2024-11-02 21:09:30815瀏覽

這篇文章的靈感很大程度上來自 Gina Banyard 在「FORUM PHP 2024」上的技術演講:

用 3 個簡單的同義詞揭開 Monad 的神秘面紗

讓我們從一些同義詞開始:

  • 容器
  • 包裝
  • 設計模式

如果你用谷歌搜尋“PHP monads”,其他概念會很快出現,例如函數式程式設計、綁定、堆疊,甚至深奧的數學(例如函子、廬半群)。

別怕。

從本質上講,Monad 是一種可以透過多種方式實現的模式。

設計模式

當您要執行一些操作時,您可以像往常一樣簡單地定義自訂物件和助理。

那麼,為什麼要費心考慮替代概念呢?

恕我直言,這仍然是一個好問題,因為您需要保持效率,但經典方法有常見的限制:

  • 操作順序很重要
  • 濕代碼
  • 例外

Monad 可以更一致地處理可選(或尚未可用)值。

Monad 與經典錯誤處理/異常

現代專案包含靜態分析工具,但 PHP 異常沒有類型化。

也就是說,工具無法偵測函數簽章中的異常,因此無法判斷程式碼是否正確處理異常。

為了測試這一點,開發團隊通常會編寫功能測試,但透過靜態分析進行早期檢測會更可靠。

來源:「Les Exception:le trou dans la raquette du typepage」(fr)

使用 Monads,您在所有情況下都會獲得一個類型化對象,例如自訂枚舉情況(例如 FileErrors::AccessDenied),因此錯誤會在系統中鍵入。

實作 Logger Monad

建立強大的日誌系統可能具有挑戰性。複製字串和呼叫很容易。

您可能會定義一個名為 log() 的自訂幫助程序,並在專案中的任何地方使用它,而不是對所有內容進行硬編碼。

這旨在保持程式碼乾燥,但可能不允許在特定情況下組合更複雜的函數。

函數式方法不包括使用這樣的全域幫助器。相反,它寧願實作一個 Monad 來包裝其他函數:

final class LoggerMonad {
    public function __construct(
        public mixed $data,
        public array $logs = [],
    ) {}

    public function bind(callable $fn) {
         $resultLoggerMonad = $fn($this->data);
         return new LoggerMonad(
             $resultLoggerMonad->data,
             [...$this->logs, ...$resultLoggerMonad->logs],
         );
   }
}

function loggify(callable $fn): Closure {
    return function ($value) use ($fn) {
        $name = (new ReflectionFunction($fn))->name;
        $log = [
            'Running '. $name .'('. var_export($value, true) .')'
        ];
        return new LoggerMonad($fn($value), $log);
    };
}

然後,您可以像這樣使用 loggify 包裝器:

function add2(int $v): int {
    return $v + 2;
}

function square(int $v): int {
    return $v * $v;
}

function multi3(int $v): int {
    return $v * 3;
}

function logIt($value, callable ...$fns) {
    $logging_fns = array_map(loggify(...), $fns);
    $monad = new LoggerMonad($value);
    foreach ($logging_fns as $fn) {
        $monad = $monad->bind($fn);
    }
    return $monad;
}

print_r(logIt(
   3,
   add2(...),
   square(...),
   multi3(...)
));

來源:“Monades simplement”,作者:Gina Banyard (fr)

什麼是綁定?

??寶貝別傷害我

Monad 旨在包裝值,可以是任何類型,包括物件和函數。

與任何其他包裝系統一樣,您會發現一個將此值作為輸入的構造函數(〜類)以及一些根據您嘗試實現的模式有其自己用途的方法。

但是,所有 Monad 都包含綁定函數。顧名思義,這是傳遞值(或回調)的地方。

無論這些回呼中發生什麼,monad 都會包裝它,這似乎是裝飾值和重構程式碼的強大方法。

程式碼可讀性更好嗎?

這顯然取決於實現,一開始很容易迷失。

但是,這種替代方法可以顯著減少 if 區塊的數量,並使回傳值更一致:

final class LoggerMonad {
    public function __construct(
        public mixed $data,
        public array $logs = [],
    ) {}

    public function bind(callable $fn) {
         $resultLoggerMonad = $fn($this->data);
         return new LoggerMonad(
             $resultLoggerMonad->data,
             [...$this->logs, ...$resultLoggerMonad->logs],
         );
   }
}

function loggify(callable $fn): Closure {
    return function ($value) use ($fn) {
        $name = (new ReflectionFunction($fn))->name;
        $log = [
            'Running '. $name .'('. var_export($value, true) .')'
        ];
        return new LoggerMonad($fn($value), $log);
    };
}

來源:fp4php - monad

優點和缺點

優點

  • ✅ 有不同用途的各種 monad : Maybe、Either、Logger、List、Reader 等
  • ✅ monad 允許使用更好的類型包裝程式碼,這可能會改善靜態分析
  • ✅ PHP 不為此提供內建構造,因此實作完全取決於開發人員

缺點

  • ❌ PHP 不為此提供內建結構(例如泛型),因此實作完全取決於開發人員
  • ❌它並沒有簡化程式碼

更進一步

  • 使用 monad、monad 轉換器和 Cats MTL 進行功能性錯誤處理
  • PHP 中的 Monad 和用法
  • PHP 函數式程式設計

包起來

希望您現在對 PHP monad 有更多了解。

當然,您不應該僅僅為了專案而添加花哨的設計模式。

此外,雖然這是一個全新的範例,但很容易錯過要點並專注於非常具體的方面,例如錯誤處理。

然而,發現新方法仍然令人耳目一新。我們需要跳出框框思考。

以上是PHP:單子的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn