在 scala 中使用 map 作为派生基元实现 Monad 特征,不满足使用 for-compression 的标准

问题描述 投票:0回答:1

我正在 scala 2.13 中构建我自己的用于教育目的的小型函数式编程库,在这样做的过程中,我无法满足用于 for 推导式的

map
操作的要求。

在这里,我使用

flatMap
pure
的组合将 map 实现为派生原语,这样我就不必每次定义 monad 实例时都为其提供实现,但不知何故,编译器对此不满意:


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

  def flatMap[A, B](fa: F[A])(g: A => F[B]): F[B]

  def map[A, B](fa: F[A])(f: A => B): F[B] =
    flatMap(fa)(f andThen pure)
}

case class Id[A](get: A)

object Monad {
  val idMonad = new Monad[Id] {
    def pure[A](a: A) = Id(a)
    def flatMap[A,B](ma: Id[A])(f: A => Id[B]) =
      f(ma.get)
  }

  val example: Id[Unit] = for {
    _ <- Id(())
  } yield ()

编译器错误:

[error] value map is not a member of fp.Id[Unit]
[error]     _ <- Id(())
[error]          ^^^^^^
Error compiling project (Scala 2.13.13, JVM (17))

scala 版本:2.13.13

有没有人看到我所做的事情中有任何明显的错误,不会接受我对

map
的定义?

我已将 map 定义为 monad 实例

Id
的方法,它允许使用 for-compression,但完全违背了我不必为
map
提供实现的意图,因为
pure
flatMap 
已实施。

case class Id[A](get: A) {
  def flatMap[B](f: A => Id[B]): Id[B] = f(get)
  // Adding map here will satisfy the requirement for for-comprehensions.
  def map[B](f: A => B): Id[B] = Id(f(get))
}

我的目标是派生原语,例如地图,因为所有 Monad 都是函子。

scala functional-programming
1个回答
0
投票

正如评论中提到的,您没有在

map
类上实现
Id
方法。此外请注意,虽然您的
map
方法需要一个实例和两个类型参数,但对于要由编译器合成的
map
方法调用,您宁愿需要在实例上定义
map
方法。 到目前为止,您所做的是在 Scala 中实现“类型类模式”的一部分。用于在类型类实例和对象实例之间“桥接”的常见机制是使用

implicit class

,以及在隐式作用域中添加类型类实例,如以下示例所示:

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

  def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]

  def map[A, B](fa: F[A])(f: A => B): F[B] =
    flatMap(fa)(f andThen pure)
}

case class Id[A](get: A)

object Monad {

  implicit class MonadOps[F[_], A](fa: F[A])(implicit val M: Monad[F]) {
    def flatMap[B](f: A => F[B]): F[B] = M.flatMap[A, B](fa)(f)
    def map[B](f: A => B): F[B] = M.map[A, B](fa)(f)
  }

  implicit val idMonad: Monad[Id] = new Monad[Id] {
    def pure[A](a: A): Id[A] = Id(a)
    def flatMap[A, B](ma: Id[A])(f: A => Id[B]): Id[B] =
      f(ma.get)
  }
}

import Monad._

val fourtytwo = for {
  twentyone <- Id(21)
} yield twentyone * 2

assert(fourtytwo.get == 42)

您可以在 
Scastie 上使用此代码

您可以在 Typelevel 博客上的

本文

上阅读有关类型类的更多信息。

© www.soinside.com 2019 - 2024. All rights reserved.