Scala日记——Monad的概念简介

前言

Scala一种结合面向对象(OO)和函数式编程(FP)的语言,学习函数式编程的话经常会遇到Monad(单子)这个概念,Monad是用于控制副作用的,这篇文章不介绍它是如何控制副作用,只介绍Monad涉及的概念。Haskell大神Philip Wadler说:

“一个单子(Monad)说白了不过就是自函子(Endofunctor)范畴上的一个幺半群(Monoid)而已,这有什么难以理解的?”

概念

范畴

范畴其实就是对特定类型的抽象,即高阶类型(也就是类型构造器)。
这里写图片描述
范畴C2就可以抽象为List高阶类型
这里写图片描述
范畴C1就可以抽象为Identity高阶类型,即

type Id[T] = T

函子(functor)

函子是体现在高阶类型(确切的说是范畴,可把范畴简单的看成高阶类型)之间的映射,如图
这里写图片描述
代码描述如下

trait Functor[F[_]] {
    def map[A,B](fa: F[A], f: A=>B): F[B]
}

具体例子

    //函子
    trait Functor[F[_]] {
      def map[A,B](fa: F[A], f: A=>B): F[B]
    }

    //范畴(高阶函数/类型构造器)
    case class My[T](e:T)

    def testMap[A,B, M <: My[A]](m:M, f: A=>B)(implicit functor:Functor[My]) = {
                functor.map(m,f)
    }

    //My[Int], 应用函数 Int=>String 得到 My[String]
    implicit object MyFunctor extends Functor[My] {
                def map[A,B](fa: My[A], f:A=>B) = My(f(fa.e))
    }

    //输出My(200ok)
    println(testMap(My(200), (x:Int)=>x+"ok"))

自函子(Endofunctor)

自函子是一类比较特殊的函子,它是一种将范畴映射到自身的函子
这里写图片描述
假设这个自函子为F,则对于 F[Int] 作用的结果仍是Int,对于函数f: Int=>String 映射的结果 F[f] 也仍是函数f
这里写图片描述
对于范畴Hask来说,如果有一个函子F,对里面的元素映射后,其结果仍属于Hask,比如我们用List这个函子

//List[A]的映射是List[List[A]...
List[A], List[List[A]], List[List[List[A]]]...

总得来说自函子就是整体结构和局部结构一致的函子

幺半群(Monoid)

群(group): G为非空集合,如果在G上定义的二元运算 *,满足

  1. 封闭性(Closure):对于任意a,b∈G,有a*b∈G
  2. 结合律(Associativity):对于任意a,b,c∈G,有(a*b)c=a(b*c)
  3. 幺元 (Identity):存在幺元e,使得对于任意a∈G,e*a=a*e=a
  4. 逆元:对于任意a∈G,存在逆元a^-1,使得a^-1*a=a*a^-1=e

则称(G,*)是群,简称G是群。

如果仅满足封闭性和结合律,则称G是一个半群(Semigroup);如果仅满足封闭性、结合律并且有幺元,则称G是一个含幺半群(Monoid)
代码描述如下

//半群
trait SemiGroup[T] {
    def append(a: T, b: T): T
}

//整数加法半群,满足封闭性和结合律
object IntSemiGroup extends SemiGroup[Int] {
    def append(a: Int, b: Int) = a + b
}

现在在半群的基础上,再增加一个幺元

//幺半群
trait Monoid[T] extends SemiGroup[T] {
    // 幺元
    def zero: T
}
//整数加法幺半群,满足封闭性和结合律,存在幺元
object IntMonoid extends Monoid[Int] {
    def append(a: Int, b: Int) = a + b
    def zero = 0
}

幺半群在fold操作中体现它的好处,fold(对折),即两两进行操作,最终合成一个。因为满足结合律所以前面的操作和后面的操作可以同时进行,大大提升效率。

def acc[T](list: List[T])(implicit m: Monoid[T]) = { 
    //可并行执行foldleft
    list.foldLeft(m.zero)(m.append) 
}

单子(Monad)

单子(Monad)是自函子(Endofunctor)范畴上的一个幺半群(Monoid)

代码解释如下

trait Monad[M[_]] {
 def unit[A](a: A): M[A]//装箱,相当于幺元
 def flatMap[A, B](fa: M[A])(f: A => M[B]): M[B]//满足自函子和半群特征
}

所以对于一个Monad,他满足半群应满足的规则:
(1)结合律/封闭性

monad.flatMap(f).flatMap(g) == monad.flatMap(v => f(v).flatMap(g))

实例:

    val multiplier : Int => Option[Int] = v => Some(v * v)
    val divider : Int => Option[Int] = v => Some(v/2)
    val original = Some(10)
    println(original.flatMap(multiplier).flatMap(divider) == original.flatMap(v => multiplier(v).flatMap(divider)))//true

(2)左幺元

unit(x).flatMap(f) == f(x)

实例:

    val item = Some(10).flatMap(multiplier)
    println(item == multiplier(10))//true   

(2)右幺元

monad.flatMap(unit) == monad

实例:

    //右幺元
    val t=multiplier(10).flatMap(v⇒Some(v))
    println(t == multiplier(10))//true

参考

Scala和范畴论 – 对Monad的一点认识
我所理解的monad(0)
Monad in Scala

猜你喜欢

转载自blog.csdn.net/qq_32198277/article/details/81034884