Scalaz(32)- Free :lift - Monad生產線

来源:http://www.cnblogs.com/tiger-xc/archive/2016/03/18/5290667.html
-Advertisement-
Play Games

在前面的討論里我們提到自由數據結構就是產生某種類型的最簡化結構,比如:free monoid, free monad, free category等等。我們也證明瞭List[A]是個free monoid。我們再看看free monad結構Free的定義:scalaz/Free.scala 我們在上


    在前面的討論里我們提到自由數據結構就是產生某種類型的最簡化結構,比如:free monoid, free monad, free category等等。我們也證明瞭List[A]是個free monoid。我們再看看free monad結構Free的定義:scalaz/Free.scala

 

/** A free operational monad for some functor `S`. Binding is done using the heap instead of the stack,
  * allowing tail-call elimination. */
sealed abstract class Free[S[_], A] {
...
  /** Return from the computation with the given value. */
  private[scalaz] case class Return[S[_], A](a: A) extends Free[S, A]

  /** Suspend the computation with the given suspension. */
  private[scalaz] case class Suspend[S[_], A](a: S[Free[S, A]]) extends Free[S, A]
...

 

我們在上一篇里證明過Free就是free monad,因為Free是個Monad而且它的結構是最簡單的了:

1、Free[S[_],A]可以代表一個運算

2、case class Return是一個數據結構。Return(a:A)代表運算結束,運算結果a存放在結構中。另一個意義就是Monad.point(a:A),把一個任意A值a升格成Free

3、case class Suspend也是另一個數據結構。Suspend(a: S[Free[S,A]])代表把下一個運算存放在結構中。如果用Monad.join(a: F[F[A]])表示,那麼裡面的F[A]應該是個Free[S,A],這樣我們才可能把運算結束結構Return[S,A](a:A)存放到Suspend中來代表下一步結束運算。

註意,Suspend(a: S[Free[S,A]])是個遞歸類型,S必須是個Functor,但不是任何Functor,而是map over Free[S,A]的Functor,也就是運算另一個Free[S,A]值。如果這個Free是Return則返回運算結果,如果是Suspend則繼續遞歸運算。

簡單來說Free是一個把Functor S[_]升格成Monad的產生器。我們可以用Free.liftF函數來把任何一個Functor升格成Monad。看看Free.liftF的函數款式就知道了:

  /** Suspends a value within a functor in a single step. Monadic unit for a higher-order monad. */
  def liftF[S[_], A](value: => S[A])(implicit S: Functor[S]): Free[S, A] =
    Suspend(S.map(value)(Return[S, A]))

liftF可以把一個S[A]升格成Free[S,A]。我們用個例子來證明:

 1 package Exercises
 2 import scalaz._
 3 import Scalaz._
 4 import scala.language.higherKinds
 5 import scala.language.implicitConversions
 6 object freelift {
 7 trait Config[+A] {
 8   def get: A
 9 }
10 object Config {
11   def apply[A](a: A): Config[A] = new Config[A] { def get = a}
12   implicit val configFunctor = new Functor[Config] {
13      def map[A,B](ca: Config[A])(f: A => B) = Config[B](f(ca.get))
14   }
15 }
16 
17 val freeConfig = Free.liftF(Config("hi config"))  //> freeConfig  : scalaz.Free[Exercises.freelift.Config,String] = Suspend(Exercises.freelift$Config$$anon$2@d70c109)

在上面的例子里Config是個運算A值的Functor。我們可以用Free.liftF把Config(String)升格成Free[Config,String]。實際上我們可以把這個必須是Functor的門檻取消,因為用Coyoneda就可以把任何F[A]拆解成Coyoneda[F,A],而Coyoneda天生是個Functor。我們先看個無法實現map函數的F[A]:

1 trait Interact[+A]  //Console交互
2 //println(prompt: String) then readLine 返回String
3 case class Ask(prompt: String) extends Interact[String]
4 //println(msg: String) 不反回任何值
5 case class Tell(msg: String) extends Interact[Unit]

由於Ask和Tell都不會返回泛類值,所以無需或者無法實現map函數,Interact[A]是個不是Functor的高階類。我們必須把它轉成Coyoneda提供給Free產生Monad:

1 Free.liftFC(Tell("hello"))                        //> res0: scalaz.Free.FreeC[Exercises.freelift.Interact,Unit] = Suspend(scalaz.Coyoneda$$anon$22@71423665)
2 Free.liftFC(Ask("how are you"))                   //> res1: scalaz.Free.FreeC[Exercises.freelift.Interact,String] = Suspend(scalaz.Coyoneda$$anon$22@20398b7c)

我們看看liftFC函數定義:

  /** A version of `liftF` that infers the nested type constructor. */
  def liftFU[MA](value: => MA)(implicit MA: Unapply[Functor, MA]): Free[MA.M, MA.A] =
    liftF(MA(value))(MA.TC)

  /** A free monad over a free functor of `S`. */
  def liftFC[S[_], A](s: S[A]): FreeC[S, A] =
    liftFU(Coyoneda lift s)

Coyoneda lift s 返回結果Coyoneda[S,A], liftFU用Unapply可以把Coyoneda[S,A]轉化成S[A]並提供給liftF。看看Unapply這段:

  /**Unpack a value of type `M0[A0, B0]` into types `[a]M0[a, B0]` and `A`, given an instance of `TC` */
  implicit def unapplyMAB1[TC[_[_]], M0[_, _], A0, B0](implicit TC0: TC[({type λ[α] = M0[α, B0]})#λ]): Unapply[TC, M0[A0, B0]] {
    type M[X] = M0[X, B0]
    type A = A0
  } = new Unapply[TC, M0[A0, B0]] {
    type M[X] = M0[X, B0]
    type A = A0
    def TC = TC0
    def leibniz = refl
  }

  /**Unpack a value of type `M0[A0, B0]` into types `[b]M0[A0, b]` and `B`, given an instance of `TC` */
  implicit def unapplyMAB2[TC[_[_]], M0[_, _], A0, B0](implicit TC0: TC[({type λ[α] = M0[A0, α]})#λ]): Unapply[TC, M0[A0, B0]] {
    type M[X] = M0[A0, X]
    type A = B0
  } = new Unapply[TC, M0[A0, B0]] {
    type M[X] = M0[A0, X]
    type A = B0
    def TC = TC0
    def leibniz = refl
  }

好了。但是又想了想,一個是Functor的Interact又是怎樣的呢?那Ask必須返回一個值,而這個值應該是個Free,實際上是代表下一個運算:

 1 package Exercises
 2 import scalaz._
 3 import Scalaz._
 4 import scala.language.higherKinds
 5 object interact {
 6 trait Interact[+A] 
 7 case class Ask[Next](prompt: String, n: String => Next) extends Interact[Next]
 8 case class Tell[Next](msg: String, n: Next) extends Interact[Next]
 9 
10 object interFunctor extends Functor[Interact] {
11   def map[A,B](ia: Interact[A])(f: A => B): Interact[B] = ia match {
12       case Tell(m,n) => Tell(m, f(n))
13       case g: Ask[A] => Ask[B](g.prompt, g.n andThen f)
14   }
15 }

所以Ask返回Next類型值,應該是個Free,代表下一個運算。

 

 

 

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 學習asp.net mvc5心得
  • 在一個控制器中,它有很幾個視圖,在視圖中有一個菜單。點擊某一菜單,它分切換至對應的視圖,當前的視圖的菜單要高亮顯示。這個要求,也有許多網友問過Insus.NET。今天決定做一個例子,供大家學習與參考。先看看效果: 上圖中,當瀏覽China的視圖時,China這行菜單是Active的,其它沒有效果,依
  • <!doctype html> <html> <head> <title>PHP函數小展示</title> </head> <body> <?php //函數定義 function name(){ echo "這是一個無參函數"; } name();//無參函數調用 function name($a
  • 在多線程的情況下,如何得到線程安全的單利模式?synchronized,內部靜態類,序列化與反序列化,靜態代碼塊,enum枚舉,多種方式總結
  • Apple 在 iOS 提供了文件共用(FileSharing)功能,讓 App 有一個對外視窗的目錄,透過 iTunes 可以任意管理這個目錄的文檔內容(可拖入文檔,也能將文檔拖出備份)。 如果 App 需要文件共用,只需要在 Delphi 的 Project Option 需要加入 UIFile
  • Linux下,當我們用mysql c api 去操作mysqldb的時候,預設情況下,程式會以Latin1這種字元集去進行sql操作,這種情況,很可能就會出現亂碼, 比如,資料庫設置的字元集是utf8, 然後我們通過程式向其中插入一條記錄,然後我們去查詢的時候,就會出現亂碼。 為瞭解決這個問題,我們
  • 本文主要介紹 Java 泛型的概念和定義,以及 Java 泛型機制的實現原理。 使用泛型程式設計,可以避免隨處可見的 Object 以及強制轉換,提高了代碼的安全性和可讀性。 類型參數(type parameters): Java 和C++一樣,通過引入類型參數進行泛型編程。 泛型類(generic
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...