我已经编写了这段代码
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
。
那么我如何在我的类型参数上指定约束,以使其始终支持平面地图?
答案 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
应该在其他地方。