2016-09-16 91 views
1

請看下面的代碼編譯很好:兩個看似相同的字符串表現不同

trait ParserError 
trait Parser[+A]{ 
    def run[A](input:A):Either[ParserError, A] 
} 

object Parser{ 
    case object ParserErrorImpl extends ParserError 

    def string(s:String):Parser[String] = new Parser[String]{ 
    def run[String](input:String) = { 
     if(input == s) Right(input) else Left(ParserErrorImpl) 
    } 
    } 
} 

object Runner{ 
    import Parser._ 
    def main(args:Array[String]):Unit={ 
    println(string("aaa").run("aaa")) 
    } 
} 

當我改變Right(input)Right(s)它帶來一個編譯錯誤:

Error:(13, 58) type mismatch; 
found : s.type (with underlying type String) 
required: String 
    def run[String](input:String) = if(input == s) Right(s) else Left(ParserErrorImpl) 
                 ^

你能解釋一下爲什麼會這樣因爲它對我來說不完全清楚?

+1

提示:無需更新與答案(實際上,它只是混淆了未來的讀者)的問題 - 只要勾選最佳答案作爲「接受」和/或upvote最好的答案。 –

+0

佔用。刪除了該更新。 –

回答

3

您正在通過(重新)在您的run方法中定義它來隱藏您的類型參數A.使特徵解析器通過類型A進行參數化,並將該類型用作運行方法的參數類型。

這裏的固定版本:

trait ParserError 
trait Parser[A]{ 
    def run(input:A):Either[ParserError, A] // <-- not run[A] !!! 
} 

object Parser{ 
    case object ParserErrorImpl extends ParserError 

    def string(s:String):Parser[String] = new Parser[String]{ 
    def run(input:String) = { 
     if(input == s) Right(input) else Left(ParserErrorImpl) 
    } 
    } 
} 

之所以這樣,是事實,你不能設置實現的方法爲String類型。你想這樣做,但實際發生的是「String」只是方法類型的另一個標識符;它也可能是「Strings」或「Stringozoid」。 @Tzach Zohar回答更豐富的解釋。

此外,我刪除了協方差以使事情更簡單,但您可能想保留它,因此您需要做好舊的下限技巧。查看@Leif Ericson的解答。

+0

非常感謝你 –

+0

這解決了這個問題,但它仍然不能解釋爲什麼它失敗 - 陰影本身不是它 –

3

當重寫一個類型參數的方法,你不能「設置」類型使用def methodName[String]String - 不管你的「通行證」作爲類型參數是不是真的會被解釋爲參數 ,而是作爲新參數名稱。在這種情況下,在:

def string(s:String):Parser[String] = new Parser[String]{ 
    def run[String](input:String) = { 
    if(input == s) Right(input) else Left(ParserErrorImpl) 
    } 
} 

當你寫def run[String],你創建一個名爲「字符串」一類型參數,而不是將使用scala.Predef.String壓倒一切的方法。現在,input保證具有該類型(不管它是什麼,可能是Int!),但s不是。

要看到這一點 - 使用不同的名稱在原來的嘗試「字符串」,而不是,編譯版本 - 並認爲它仍然編譯:

def string(s:String):Parser[String] = new Parser[String]{ 
    def run[SomeUnknownType](input:SomeUnknownType) = { 
    if(input == s) Right(input) else Left(ParserErrorImpl) 
    } 
} 

要解決這個問題 - 在這種情況下,你可以擺脫方法的類型參數,並使用類的參數,如@slouc建議的那樣 - 您將它們命名爲A,但它們是兩個不同的參數。

+0

如果這是一個更好的版本,你可以查看UPDATE下的代碼。我將刪除此UPDATE部分。對我而言,瞭解如何使這種特質協變並且我認爲現在好了,這一點很重要。 –

+0

是的,更新看起來是正確的,因爲它使用一個新的類型參數綁定成一個超類型的字符串'[B>:String]'而不是直接使用'[String]',它實際上並沒有將類型綁定到什麼... –

+0

謝謝你,Tzach –

2

實際上,你可以保持協方差,同時還使得代碼編譯:

trait ParserError 

trait Parser[+A] { 
    def run[B >: A](input: B): Either[ParserError, B] 
} 

object Parser { 

    case object ParserErrorImpl extends ParserError 

    def string(s: String): Parser[String] = new Parser[String] { 
    def run[B >: String](input: B) = { 
     if (input == s) Right(s) else Left(ParserErrorImpl) 
    } 
    } 
} 

object Runner { 

    import Parser._ 

    def main(args: Array[String]): Unit = { 
    println(string("aaa").run("aaa")) 
    } 
} 
+0

謝謝雷夫,我已經做到了這一點 –

相關問題