2010-07-19 3 views
8

와 모나드 :리더 나는이 같은 scalaz으로 리더 모나드를 정의하려고 Scalaz

import scalaz._ 
import Scalaz._ 

final class Reader[E,A](private[Reader] val runReader: E => A) 

object Reader { 
    def apply[E,A](f: E => A) = new Reader[E,A](f) 
    def env[E]: Reader[E,E] = Reader(identity _) 

    implicit def ReaderMonad[E] = new Monad[PartialApply1Of2[Reader,E]#Apply] { 
    def pure[A](a: => A) = Reader(_ => a) 

    def bind[A,B](m: Reader[E,A], k: A => Reader[E,B]) = 
     Reader(e => k(m.runReader(e)).runReader(e)) 
    } 
} 


object Test { 
    import Reader._ 

    class Env(val s: String) 

    def post(s: String): Reader[Env, Option[String]] = 
    env >>= (e => if (e.s == s) some(s).pure else none.pure) 
} 

하지만 컴파일러 오류 얻을 : 왜 그

reader.scala:27: reassignment to val 
    env >>= (e => if (e.s == s) some(s).pure else none.pure) 
     ^

입니다?

감사합니다, 레위

답변

16

이 오류는 심지어 스칼라의 기준에 의해 상당히 불투명하다. =으로 끝나는 메서드 이름은 특별히 처리됩니다. 즉, 처음에는 정상 식별자로 간주되며 실패하면 자체 할당으로 확장됩니다. 당신이 당신의 프로그램의 구문 해석에 대한 혼란이 있다면

scala> def env[A] = 0 
env: [A]Int 

scala> env >>= 0 
<console>:7: error: reassignment to val 
     env >>= 0 
     ^

scala> env = env >> 0 
<console>:6: error: reassignment to val 
     env = env >> 0 
     ^

, 그것은 무슨 일이 일어나고 있는지 볼 수 scalac -Xprint:parser를 실행하는 것이 좋습니다. 마찬가지로 프로그램 변환의 후반 단계를 보려면 -Xprint:typer 또는 -Xprint:jvm을 사용할 수 있습니다.

Reader에서 >>= 번으로 전화하는 방법은 무엇입니까? 먼저, 형식 인수 Envenv에 명시 적으로 전달해야합니다. 그 결과로 Reader[Env, Env]MA[M[_], A]으로 변환되어야합니다. 간단한 형식 생성자의 경우에는 암시 적 변환 MAs#ma이면 충분합니다. 그러나 두 개의 매개 변수 형식 생성자 Reader은 부분적으로 적용해야합니다. 즉 유추 할 수 없으며 특정 암시 적 변환을 제공해야합니다.

Adriaan이 여분의 오후를 implement higher-order unification for type constructor inference으로 찾으면 상황이 크게 향상됩니다. :)

그 때까지는 여기에 코드가 있습니다. 몇 가지 덧글이 인라인입니다.

import scalaz._ 
import Scalaz._ 

final class Reader[E, A](private[Reader] val runReader: E => A) 

object Reader { 
    def apply[E, A](f: E => A) = new Reader[E, A](f) 

    def env[E]: Reader[E, E] = Reader(identity _) 

    implicit def ReaderMonad[E]: Monad[PartialApply1Of2[Reader, E]#Apply] = new Monad[PartialApply1Of2[Reader, E]#Apply] { 
    def pure[A](a: => A) = Reader(_ => a) 

    def bind[A, B](m: Reader[E, A], k: A => Reader[E, B]) = 
     Reader(e => k(m.runReader(e)).runReader(e)) 
    } 

    // No Higher Order Unification in Scala, so we need partially applied type constructors cannot be inferred. 
    // That's the main reason for defining function in Scalaz on MA, we can create one implicit conversion 
    // to extract the partially applied type constructor in the type parameter `M` of `MA[M[_], A]`. 
    // 
    // I'm in the habit of explicitly annotating the return types of implicit defs, it's not strictly necessary 
    // but there are a few corner cases it pays to avoid. 
    implicit def ReaderMA[E, A](r: Reader[E, A]): MA[PartialApply1Of2[Reader, E]#Apply, A] = ma[PartialApply1Of2[Reader, E]#Apply, A](r) 
} 


object Test { 
    import Reader._ 

    class Env(val s: String) 

    def post(s: String): Reader[Env, Option[String]] = 
    // Need to pass the type arg `Env` explicitly here. 
    env[Env] >>= {e => 
     // Intermediate value and type annotation not needed, just here for clarity. 
     val o: Option[String] = (e.s === s).guard[Option](s) 
     // Again, the partially applied type constructor can't be inferred, so we have to explicitly pass it. 
     o.pure[PartialApply1Of2[Reader, Env]#Apply] 
    } 
} 
+2

감사합니다. 그건 속임수 야. 내가 스칼라가 실제로 나를 실망시키는 것은 사실을 기능적 언어로 사용하려고 시도 할 때 고백해야한다. 왜냐하면 스칼라는 나에게 거대한 해킹 한 느낌을주기 때문이다. –

+4

하스켈에서오고 있습니다. Scala는 Hindley-Milner 추론과 경쟁 할 수 없으며 순결을 시행하지 않으며 기본적으로 엄격합니다. 그것은 JVM interop을 가지고 있습니다. 암시 적 params는 타입 클래스로 인해 까다로운 몇 가지를 인코딩 할 수 있습니다. – retronym