2016-11-20 147 views
4

我想了解免费单子。所以在教程的帮助下,我写了一个玩具的例子,现在我不明白它为什么编译。这里是:为什么此代码与免费的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) 
    } 
} 

我在说解释器的应用方法。它应该返回Option [A],但是我可以在这里返回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) 
    } 

回答

3

apply方法应该在那里A由参数的类型确定返回Option[A]。也就是说,如果参数的类型为Operation[Unit],那么结果也应该是Option[Unit]等等。

现在你的身体完全符合那份合约。是的,您确实有一种情况,即您返回Option[Unit]而不是一般Option[A],但只有在参数是Print的实例并因此Operation[Unit]时才这样做。当您的参数是Operation[Unit]时,您只能返回Option[Unit],所以合同不会中断。 ReadString也是如此。请注意,如果您在Read的情况下返回了Option[Unit],那将是一个错误,因为您现在要返回的参数类型不是参数的类型。

所以这就是为什么代码在语义上是正确的,但它为什么编译?这是因为Scala类型检查器(与IntelliJ的近似值不同)足够智能以在模式匹配时考虑附加类型信息。也就是说,在case Print中,它知道您刚刚匹配Operation[A]类型的值与Operation[Unit]类型的模式,因此它在案例正文中指定A = Unit


关于你提到的更新:

case Print(s) => Some(s) 

在这里,我们有Operation[Unit]型模式(记住Print扩展Operation[Unit]),所以我们应该得到Option[Unit]类型的结果,但Some(s)具有类型Option[String]。所以这是一种类型不匹配。

case Read() => Some(Unit) 

首先UnitUnit类型的伴侣的对象,所以它有它自己的类型,而不是键入Unit。类型Unit的唯一值是()

除此之外,它是与上述相同的情况:该图案具有类型Operation[String],所以结果应该是Operation[String],不Operation[Unit](或Operation[Unit.type])。

+0

哇,斯卡拉克真的很聪明。谢谢。 –

+0

我已经接受了答案,但你能看看我的更新吗? –

+0

我的不好,抱歉。它会像你悲伤一样工作。我删除了更新。 –