为什么这段带有免费 monad 解释器的代码可以编译?
Why does this code with free monad interpreter compile?
我正在尝试了解自由 monad。因此,在教程的帮助下,我编写了玩具示例来玩,现在我不明白为什么它会编译。这是:
import cats.free.Free
import cats.instances.all._
import cats.~>
trait Operation[+A]
case class Print(s: String) extends Operation[Unit]
case class Read() extends Operation[String]
object Console {
def print(s: String): Free[Operation, Unit] = Free.liftF(Print(s))
def read: Free[Operation, String] = Free.liftF(Read())
}
object Interpreter extends (Operation ~> Option) {
// why does this compile?
override def apply[A](fa: Operation[A]): Option[A] = fa match {
case Print(s) => Some(println(s))
case Read() => Some(readLine())
}
}
object Main {
def main(args: Array[String]) {
val program = for {
_ <- Console.print("What is your name?")
name <- Console.read
_ <- Console.print(s"Nice to meet you $name")
} yield ()
program.foldMap(Interpreter)
}
}
我说的是 Interpreter 的 apply 方法。它应该 return Option[A],但我可以 return Option[Unit] 和 Option[String],所以我认为它应该是一个编译错误。但事实并非如此。这段代码可以编译并运行(尽管 Idea 告诉我这是一个错误)。这是为什么?
UPD:但为什么不能编译?
def test[A](o: Operation[A]): Option[A] = o match {
case Print(s) => Some(s)
case Read() => Some(Unit)
}
您的 apply
方法应该 return Option[A]
其中 A
由参数的类型决定。也就是说,如果参数的类型是 Operation[Unit]
,结果也应该是 Option[Unit]
等等。
现在您的 body 完全遵守了该合同。是的,你确实有这样的情况,你 return 一个 Option[Unit]
而不是一般的 Option[A]
,但你只有在参数是 Print
的实例时才这样做,因此Operation[Unit]
。也就是说,当参数是 Operation[Unit]
时,你只会 return 一个 Option[Unit]
,所以合同没有被破坏。 Read
和 String
也是如此。请注意,如果您在 Read
的情况下 returned 一个 Option[Unit]
,那将是一个错误,因为您现在 returning 的类型不是参数。
这就是为什么代码在语义上是正确的,但为什么它可以编译?这是因为 Scala 类型检查器(不像 IntelliJ 的近似)足够聪明,可以在模式匹配时考虑额外的类型信息。也就是说,在 case Print
中,它知道您刚刚将类型 Operation[A]
的值与类型 Operation[Unit]
的模式匹配,因此它在 case 的 body.
关于您的更新:
case Print(s) => Some(s)
这里我们有一个 Operation[Unit]
类型的模式(记住 Print
扩展了 Operation[Unit]
),所以我们应该得到一个 Option[Unit]
类型的结果,但是 Some(s)
的类型为 Option[String]
。所以这是类型不匹配。
case Read() => Some(Unit)
首先Unit
它是Unit
类型的同伴object,所以它有自己的类型,而不是Unit
类型。 Unit
类型的唯一值是 ()
.
除此之外,情况与上述相同:模式的类型为 Operation[String]
,因此结果应为 Operation[String]
,而不是 Operation[Unit]
(或 Operation[Unit.type]
).
我正在尝试了解自由 monad。因此,在教程的帮助下,我编写了玩具示例来玩,现在我不明白为什么它会编译。这是:
import cats.free.Free
import cats.instances.all._
import cats.~>
trait Operation[+A]
case class Print(s: String) extends Operation[Unit]
case class Read() extends Operation[String]
object Console {
def print(s: String): Free[Operation, Unit] = Free.liftF(Print(s))
def read: Free[Operation, String] = Free.liftF(Read())
}
object Interpreter extends (Operation ~> Option) {
// why does this compile?
override def apply[A](fa: Operation[A]): Option[A] = fa match {
case Print(s) => Some(println(s))
case Read() => Some(readLine())
}
}
object Main {
def main(args: Array[String]) {
val program = for {
_ <- Console.print("What is your name?")
name <- Console.read
_ <- Console.print(s"Nice to meet you $name")
} yield ()
program.foldMap(Interpreter)
}
}
我说的是 Interpreter 的 apply 方法。它应该 return Option[A],但我可以 return Option[Unit] 和 Option[String],所以我认为它应该是一个编译错误。但事实并非如此。这段代码可以编译并运行(尽管 Idea 告诉我这是一个错误)。这是为什么?
UPD:但为什么不能编译?
def test[A](o: Operation[A]): Option[A] = o match {
case Print(s) => Some(s)
case Read() => Some(Unit)
}
您的 apply
方法应该 return Option[A]
其中 A
由参数的类型决定。也就是说,如果参数的类型是 Operation[Unit]
,结果也应该是 Option[Unit]
等等。
现在您的 body 完全遵守了该合同。是的,你确实有这样的情况,你 return 一个 Option[Unit]
而不是一般的 Option[A]
,但你只有在参数是 Print
的实例时才这样做,因此Operation[Unit]
。也就是说,当参数是 Operation[Unit]
时,你只会 return 一个 Option[Unit]
,所以合同没有被破坏。 Read
和 String
也是如此。请注意,如果您在 Read
的情况下 returned 一个 Option[Unit]
,那将是一个错误,因为您现在 returning 的类型不是参数。
这就是为什么代码在语义上是正确的,但为什么它可以编译?这是因为 Scala 类型检查器(不像 IntelliJ 的近似)足够聪明,可以在模式匹配时考虑额外的类型信息。也就是说,在 case Print
中,它知道您刚刚将类型 Operation[A]
的值与类型 Operation[Unit]
的模式匹配,因此它在 case 的 body.
关于您的更新:
case Print(s) => Some(s)
这里我们有一个 Operation[Unit]
类型的模式(记住 Print
扩展了 Operation[Unit]
),所以我们应该得到一个 Option[Unit]
类型的结果,但是 Some(s)
的类型为 Option[String]
。所以这是类型不匹配。
case Read() => Some(Unit)
首先Unit
它是Unit
类型的同伴object,所以它有自己的类型,而不是Unit
类型。 Unit
类型的唯一值是 ()
.
除此之外,情况与上述相同:模式的类型为 Operation[String]
,因此结果应为 Operation[String]
,而不是 Operation[Unit]
(或 Operation[Unit.type]
).