2016-11-20 7 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의 근사치와 달리)가 패턴 일치가 발생할 때 추가 유형 정보를 고려할만큼 충분히 똑똑하기 때문입니다. 즉, case Print에서 Operation[A] 유형의 값과 Operation[Unit] 유형의 패턴을 일치 시켰으므로 A = Unit을 케이스 본문에 할당합니다.

다음
case Print(s) => Some(s) 

우리가 유형 Operation[Unit]의 패턴을 가지고 (PrintOperation[Unit]를 확장 기억), 그래서 우리는 유형 Option[Unit]의 결과를 얻을해야하지만 Some(s)Option[String]을 입력있다 : 당신의 갱신에 관한


. 그래서 그것은 타입 불일치입니다.

case Read() => Some(Unit) 

모든 Unit 그것을 Unit 유형의 동반자 개체의 첫 번째, 그래서 자신의 유형이는 Unit를 입력하지. Unit 유형의 유일한 값은 ()입니다. 것을 제외

는, 상기와 동일한 상황이다 : 패턴은 Operation[String] 입력을 가지므로 그 결과는 Operation[String]하지 Operation[Unit] (또는 Operation[Unit.type])이어야한다.

+0

와우, scalac은 정말 똑똑합니다. 고맙습니다. –

+0

이미 답변을 수락했으나 내 업데이트를 볼 수 있습니까? –

+0

죄송합니다. 죄송합니다. 슬픈 것처럼 작동합니다. 업데이트를 삭제했습니다. –