Functor Monad 及 他们与范畴的关系

单子和函子构成了构成了函数式编程的基本概念的基础构造元素。

函子(Functor)

Functor是从一个范畴到另一范畴的转换,并且其亦可转换/保持态射(morphisrm)。morphisrm是指一个范畴里的值到同个范畴里的另一个值的变化。 在类型的范畴里面--这是计算机科学最常用的范畴-- 一个态射是一个把某个类型转换成另一个类型的函数。

我们围绕Config展开讨论

trait Config[+A] {
    def map[B](f: A => B): Config[B]
    def flatMap[B](f: A => Config[B]): Config[B]
    def get: A
}

我们暂时假设 map和flatMap 方法在类上已定义

object Config{
    def apply[A](data: => A) = new Config{
        def get = data
    }
}

一个转换必须在转换是保留所有的态射才能称为函子转换,也就是说如果在第一个范畴里面有个操作类型的函数,那么我们也应该有个转换后的函数能够操作转换后的类型。比如说我们有个把String转换成Int的函数,那么我们也可以把Config[String]转换成Config[Int].这正是Config的map方法提供的功能。

我们用个接口表现这样的功能。

trait Functor[T[_]] {
    def apply[A](x: A):T[A]
    def map[A,B](x: T[A])(f: A => B): T[B]
}

apply方法为Functor赋予了第一个属性--对于任意类型A能够在新范畴里面构造一个类型T[A]。 map方法赋予了第二个属性--给定一个转换后的类型T[A]和一个在原类型里面的态射A => B,能够创建一个T[B] 类型的结果。也就是说我们有一个新函数接受T[A], 返回T[B].

我们来为Config实现Functor接口

object ConfigAsFunctor extends Functor[Config]{
    def apply[A](x: A):Config[A] = Config(x)
    def map[A,B](x: Config[A])(f: A => B): Config[B]= x.map(f)
}

Config的Functor实现定义为 apply调用Config伴生对象的apply方法,map方法简单的代理给Functor的map方法。

我们来造一点语法糖,以便Functor上的map方法看起来像定义在原始类型上。

implict def functorOps[F[_]:Functor, A](ma: F[A]) = new {
    val functor = implicitly[Functor[F]]
    final def map[B](f: A => B): F[B] = functor.map(ma)(f)
}

单子(Monad)

单子是一种将函数组合应用的方法,前提是该函子必须是内函子,该函子将其范畴内的概念和态射依然转换到相同的范畴内。

在计算机科学内,单子经常用来表示计算。单子能用来把程序的执行行为抽象出来,比如有用来处理并行、异常与副作用的单子。

我们来看一下编程上对单子的定义

trait Monad[T[_]]{
    def flatten[A](f:T[T[A]]):T[A]
    def flatMap[A,B](x: T[A])(f: A => T[B]
     )(implicit func: Functor[T]):T[B] = {
        flatten(func.map(x, f))
     }
}

Monad特质定义了 faltten 和 faltMap 两种方法。flatten接受一个包裹了两层的类型转换成包了一层的类型。 比如说 List Monad 能够将一个列表的列表,变成一个包含原本子列表所有元素的列表。 flatMap是Monad 提供的一个便利函数,实际上是把flatten 和 map 串联起来以方便使用。

你可能感兴趣的:(scala,monad,函数式,范畴,functor)