2016-11-20 6 views
4

私はフリーモナドを理解しようとしています。チュートリアルの助けを借りて、私はおもちゃの例を書いていましたが、今はなぜそれがコンパイルされるのかわかりません。ここにあります:フリーモナドインタプリタでこのコードをコンパイルするのはなぜですか?

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の適用方法について話しています。 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[A]の代わりにOption[Unit]を返すケースがありますが、引数がPrintのインスタンスであり、したがってOperation[Unit]のインスタンスである場合にのみ行います。引数がOperation[Unit]のときはOption[Unit]しか返されないので、契約は破られません。 ReadStringについても同様です。 Readの場合にOption[Unit]を返した場合、引数の型以外の型が返されるため、エラーになります。

なぜコードが意味的に正しいのですが、なぜそれがコンパイルされますか?これは、IntelliJの近似とは異なり、Scala型のチェッカーは、パターンマッチングの際に追加の型情報を考慮に入れるほどスマートです。つまり、case Printでは、タイプOperation[A]の値とタイプOperation[Unit]のパターンが一致していることがわかりますので、A = Unitをケースの本体に割り当てます。あなたの更新について


ここ
case Print(s) => Some(s) 

我々は(PrintOperation[Unit]を拡張することを覚えておいてください)タイプOperation[Unit]のパターンを持っているので、我々はタイプOption[Unit]の結果を取得する必要がありますが、Option[String]を入力Some(s)ています。それは型の不一致です。

case Read() => Some(Unit) 

UnitタイプのすべてのUnitそれコンパニオンオブジェクトの第一に、それは独自の型を持つので、Unitを入力していません。タイプUnitの唯一の値は()です。それ以外の点から

、上記と同様の状況は次のとおりパターンOperation[String]型を持つので、結果はOperation[String]なく、Operation[Unit](又はOperation[Unit.type])であるべきです。

+0

うわー、スカラックは本当にスマートです。ありがとうございました。 –

+0

私はすでに答えを受け入れましたが、私の更新を見ることはできますか? –

+0

私の悪い、申し訳ありません。あなたが悲しんでいるように機能します。私は更新を削除しました。 –

関連する問題