确保我的特征的类型参数是Monad

时间:2018-11-10 20:04:58

标签: scala

我已经编写了这段代码

trait Input[F[_]] {
    def read: F[String]
    def write(str: String) : F[Unit]
    def getName : F[String] = for {
        _ <- write("What is your name")
        name <- read
    } yield name
}

此代码显然无法编译,因为编译器无法知道F类型支持平面地图。所以我将代码更改为

import scalaz._
import Scalaz._
trait Input[F[_] : Monad] {
    def read: F[String]
    def write(str: String) : F[Unit]
    def getName : F[String] = for {
        _ <- write("What is your name")
        name <- read
    } yield name
}

但是现在我得到了编译时错误traits cannot have type parameters with context bounds

那么我如何在我的类型参数上指定约束,以使其始终支持平面地图?

1 个答案:

答案 0 :(得分:1)

trait Input[F[_]: Monad]将创建一个隐式构造函数参数,并且特征不能具有构造函数参数(直到Scala 3)。 def testFunc[F[_]: Monad]将创建一个隐式参数。例如:

def testFunc[F[_]: Monad](arg: Int) = ???
class TestClass[F[_]: Monad] {}

会起作用,因为它被翻译为:

def testFunc[F[_]](arg: Int)(implicit ev: Monad[F]) = ???
class TestClass[F[_]](implicit val ev: Monad[F]) {}

[F[_]: Monad][F[_]]implicit val ev: Monad[F]的语法糖。而且trait直到Scala 3之前都没有构造函数可以传递参数。

对于您的情况,例如,如果您确实需要在特征内将F约束为Monad,则:

trait Input[F[_]] {
  val M: Monad[F]

  def read: F[String]
  def write(str: String) : F[Unit]
  def getName : F[String] = M.flatMap(write("What is your name"))(_ => read)
}

即您对实施者说:“一旦拥有Input,您就可以实施Monad[F]”。然后,您可以像这样使用它:

object Main extends App{

  class IOInput extends Input[IO] {
    override val M: Monad[IO] = Monad[IO]
    override def read: IO[String] = IO("red")
    override def write(str: String): IO[Unit] = IO(println(s"write: $str"))
  }

  val impl = new IOInput

  println(impl.getName.unsafeRunSync())
}

P.S。但是对我来说,似乎有些问题。您是在特征Input中定义效果,并在同一特征中正确使用它们。至少对我来说很奇怪。可能getName应该在其他地方。