scala - Reader monad - 它如何符合 Monad 接口(interface)?

标签 scala monads category-theory reader-monad

我正在学习范畴论。 我理解 reader monad 的概念,它甚至很容易实现:

case class Reader[DEP, A](g: DEP => A) {
  def apply(dep: DEP): A = g(dep)

  def map[B](f: A => B): Reader[DEP, B] = Reader(dep => f(apply(dep)))

  def flatMap[B](f: A => Reader[DEP, B]): Reader[DEP, B] = Reader(dep => f(apply(dep)) apply dep)
}

但是,我在限制某些通用 Monad 接口(interface)的情况下实现它时遇到问题,即

trait Monad[A] {
  def pure(a: A): Monad[A]

  def map[B](f: A => B): Monad[B]

  def flatMap[B](f: A => Monad[B]): Monad[B]
}

让我们暂时忘记还有一个 applicative 或 functor,让我们把这 3 个方法放在这里。

现在,有了这个接口(interface),我在实现 ReaderMonad 时遇到了问题。 map 方法非常简单,但是 pure 和 flatMap 呢? Reader 上的 pure 到底意味着什么? 为了实现 flatMap,我需要有一个从 A 到 Reader[DEP, B] 的函数,但我有 A => Monad[B],因此我无法访问 apply。

case class Reader[DEP, A](g: DEP => A) extends Monad[A] {
  def apply(dep: DEP): A = g(dep)

  override def pure(a: A): Reader[DEP, A] = Reader(_ => a) // what does it even mean in case of Reader

  override def map[B](f: (A) => B): Reader[DEP, B] = Reader(dep => f(apply(dep)))

  override def flatMap[B](f: (A) => Monad[B]): Reader[DEP, B] = ??? // to implement it, I need f to be (A) => Reader[DEP, B], not (A) => Monad[B]
}

在scala中可以这样实现吗?我尝试使用自绑定(bind)类型,但它也不起作用。 我知道像 scalaz 或 cats 这样的库使用类型类来实现这些类型,但这只是出于教育目的。

最佳答案

正如您在尝试实现 flatMap 时发现的那样,声明 Monad 特征的问题是您会丢失定义的特定 monad 类型链接操作。定义 Monad 特征的常用方法是通过正在定义 monad 实例的类型构造函数来参数化它,例如

trait Monad[M[_]] {
    def pure[A](a: A): M[A]
    def map[A, B](f: A => B, m: M[A]): M[B]
    def flatMap[A, B](f: A => M[B], m : M[A]): M[B]
}

所以M是一个一元类型构造函数,例如ListOption。您可以将 Reader[DEP, A] 视为依赖于某种环境类型 DEP 的计算,该类型返回 A 类型的值。由于它有两个类型参数,因此您需要在定义 monad 实例时修复环境参数类型:

case class Reader[DEP, A](g: DEP => A)

class ReaderMonad[DEP]() extends Monad[({type t[X] = Reader[DEP, X]})#t] {
    def pure[A](a: A) = Reader[DEP, A](_ => a)
    def map[A, B](f: A => B,m: Reader[DEP,A]): Reader[DEP,B] = Reader(env => f(m.g(env)))
    def flatMap[A, B](f: A => Reader[DEP,B],m: Reader[DEP,A]): Reader[DEP,B] = Reader(env => f(m.g(env)).g(env))
}

({type t[X] = Reader[DEP, X]})#ttype lambda用于部分应用 Reader[DEP, A] 的两个参数之一。

现在pure返回一个Reader,它忽略环境并直接返回给定值。

flatMap 构造一个 Reader,运行时将运行内部计算,使用结果构造下一个计算并在相同的环境中运行它。

关于scala - Reader monad - 它如何符合 Monad 接口(interface)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42055351/

相关文章:

haskell - 转换具有两个以上 Action 的 "do"表示法以使用绑定(bind)函数

lisp - 范畴论术语中的 Lisp `quote` 特殊形式是什么?

list - 非空列表comonad

haskell - 如何将范畴论图与多元函数一起使用?

haskell - 在 monad 的上下文中是否一定要使用 do 符号?

f# - 为什么我要使用即将到来的 let! ... 和!句法?

python - 用 Python 编写解释器。 isinstance 是否被认为是有害的?

scala - 在列表中查找仅具有第一个元素值的对

scala - PlayFramework:如何注入(inject)数据库对象

scala - 为什么我不能将case对象用作多态类型