Scala 为什么这段代码使用免费的monad解释器编译?

Scala 为什么这段代码使用免费的monad解释器编译?,scala,free-monad,Scala,Free 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 Consol

我在试着理解自由单子。在教程的帮助下,我编写了一个玩具示例,现在我不明白为什么要编译它。这是:

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)
  }
}

您的
apply
方法应该返回
选项[A]
,其中
A
由参数的类型决定。也就是说,如果参数的类型为
Operation[Unit]
,则结果也应该是
选项[Unit]
,依此类推

现在你的身体完美地遵守了这个契约。是的,您确实有返回
选项[Unit]
而不是常规
选项[a]
的情况,但只有当参数是
打印的实例,因此是
操作[Unit]
时,您才会这样做。也就是说,只有当参数是
操作[Unit]
时,您才会返回一个
选项[Unit]
,因此合同不会被破坏。
Read
String
也是如此。请注意,如果在
Read
的情况下返回了
选项[Unit]
,那将是一个错误,因为您现在返回的不是参数的类型

这就是为什么代码在语义上是正确的,但为什么它要编译?这是因为Scala类型检查器(与IntelliJ的近似值不同)足够聪明,可以在模式匹配时考虑额外的类型信息。也就是说,在
案例打印
中,它知道您刚刚将
操作[a]
类型的值与
操作[Unit]
类型的模式进行了匹配,因此它在案例主体内部分配
a=Unit


关于您的更新:

case Print(s) => Some(s)
这里我们有一个类型为
Operation[Unit]
(记住
Print
扩展了
Operation[Unit]
),因此我们应该得到类型为
Option[Unit]
的结果,但是
一些
具有类型
Option[String]
。这是一种类型不匹配

case Read() => Some(Unit)
首先,
Unit
它是
Unit
类型的伴生对象,因此它有自己的类型,而不是类型
Unit
。类型
单元的唯一值是
()


除此之外,情况与上面相同:模式的类型为
Operation[String]
,因此结果应该是
Operation[String]
,而不是
Operation[Unit]
(或
Operation[Unit.type]
)。

哇,scalac真的很聪明。谢谢。我已经接受了答案,但是你能看看我的更新吗?我的错,对不起。它在你悲伤的时候起作用。我删除了更新。因为您已经回答了我的更新,所以我将其写回:)再次抱歉,谢谢您的明确回答。
case Read() => Some(Unit)