2012-09-26 28 views
17

Gần đây, tôi đã gặp một thông báo lỗi trình biên dịch lạ (đối với tôi). Xét đoạn mã sau:lớp A có một tham số kiểu, nhưng loại B có một số

trait Foo { 
    type Res <: Foo 
    type Bar[X <: Res] 
} 

class MyFoo extends Foo { 
    override type Res = MyFoo 
    override type Bar[X <: Res] = List[X] 
} 

type FOO[F <: Foo, R <: Foo, B[_ <: R]] = F { type Res = R; 
               type Bar[X <: R] = B[X] } 

def process[F <: Foo, R <: Foo, B[_ <: R]](f: FOO[F, R, B]) {} 

Bây giờ, nếu tôi muốn gọi process phương pháp tôi phải viết một cách rõ ràng các thông số type:

process[MyFoo, MyFoo, List](new MyFoo) // fine 

Nếu tôi viết:

process(new MyFoo) 

hoặc

process((new MyFoo): FOO[MyFoo, MyFoo, List]) 

Tôi nhận được thông báo lỗi sau:

loại phỏng đoán của các đối số kiểu (MyFoo, MyFoo, List [X]) không phù hợp với các loại tham số loại mong muốn (loại F, loại R, loại B). Liệt kê các tham số kiểu của [X] không khớp với các tham số được mong đợi của loại B: Danh sách lớp có một tham số kiểu, nhưng kiểu B có một tham số kiểu

Tại sao trình biên dịch không thể suy ra các loại (mặc dù tôi đã nói rõ ràng) tại thông số cuộc gọi)? Và class List has one type parameter, but type B has one có nghĩa là gì? Cái gì đó có một cái, nhưng cái kia có cũng là một, và đó là lý do tại sao chúng không khớp với nhau ???

+0

Tôi đang sử dụng scala 2.9.3-20120917-121530-db16547873 –

Trả lời

2

Nếu chúng ta nhìn vào trình biên dịch Scala, các nguồn có thể giúp chúng ta hiểu vấn đề là gì. Tôi chưa bao giờ đóng góp cho trình biên dịch Scala, nhưng tôi đã tìm thấy các nguồn rất dễ đọc và tôi đã điều tra về điều đó.

Lớp học chịu trách nhiệm về suy luận kiểu là scala.tools.nsctypechecker.Infer mà bạn có thể tìm thấy đơn giản bằng cách tìm trong nguồn trình biên dịch Scala cho một phần lỗi của bạn. Bạn sẽ tìm thấy đoạn sau:

/** error if arguments not within bounds. */ 
    def checkBounds(pos: Position, pre: Type, owner: Symbol, 
        tparams: List[Symbol], targs: List[Type], prefix: String) = { 
     //@M validate variances & bounds of targs wrt variances & bounds of tparams 
     //@M TODO: better place to check this? 
     //@M TODO: errors for getters & setters are reported separately 
     val kindErrors = checkKindBounds(tparams, targs, pre, owner) 

     if(!kindErrors.isEmpty) { 
     error(pos, 
      prefix + "kinds of the type arguments " + targs.mkString("(", ",", ")") + 
      " do not conform to the expected kinds of the type parameters "+ tparams.mkString("(", ",", ")") + tparams.head.locationString+ "." + 
      kindErrors.toList.mkString("\n", ", ", "")) 
     } 

Vì vậy, bây giờ, điểm là hiểu tại sao checkKindBounds(tparams, targs, pre, owner) trả về các lỗi đó.Nếu bạn đi xuống chuỗi gọi phương thức, bạn sẽ thấy rằng các checkKindBounds gọi phương thức khác

val errors = checkKindBounds0(tparams, targs, pre, owner, true) 

Bạn sẽ thấy vấn đề được kết nối với kiểm tra giới hạn của loại-kinded cao hơn, tại dòng 5784, bên checkKindBoundsHK:

if (!sameLength(hkargs, hkparams)) { 
     if (arg == AnyClass || arg == NothingClass) (Nil, Nil, Nil) // Any and Nothing are kind-overloaded 
     else {error = true; (List((arg, param)), Nil, Nil) } // shortcut: always set error, whether explainTypesOrNot 
     } 

Xét nghiệm này không được thông qua, có vẻ như trong trình gỡ lỗi của tôi:

hkargs$1 = {[email protected]}"List()" 
arg$1 = {[email protected]}"class List" 
param$1 = {[email protected]}"type B" 
paramowner$1 = {[email protected]}"method process" 
underHKParams$1 = {[email protected]}"List(type R)" 
withHKArgs$1 = {[email protected]}"List()" 
exceptionResult12 = null 
hkparams$1 = {[email protected]}"List(type R)" 

Vì vậy, dường như có một cao param kinded, loại R, nhưng không có cung cấp là d giá trị cho điều đó.

Nếu bạn thực sự quay trở lại để checkKindBounds, bạn sẽ thấy rằng sau khi đoạn:

val (arityMismatches, varianceMismatches, stricterBounds) = (
     // NOTE: *not* targ.typeSymbol, which normalizes 
     checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO) 
    ) 

các arityMismatches chứa một danh sách tuple, B. Và bây giờ bạn cũng có thể thấy rằng các thông báo lỗi là sai:

loại tin cậy của các đối số kiểu (MyFoo, MyFoo, Danh sách [X]) không phù hợp với các loại dự kiến ​​của các tham số kiểu (loại F, gõ R, loại B). Danh sách [X] 's tham số kiểu không phù hợp với mong đợi thông số loại B: Danh sách lớp có một số loại, nhưng loại B có ZERO

Trong thực tế, nếu bạn đặt một breakpoint tại dòng 5859 trên sau gọi

checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO) 

bạn có thể thấy rằng

tparam = {[email protected]}"type B" 
targ = {[email protected]}"List[X]" 

Kết luận:

Vì một số lý do, khi giao dịch với các loại phức tạp cao cấp hơn như của bạn, suy luận trình biên dịch Scala bị giới hạn. Tôi không biết nó đến từ đâu, có thể bạn muốn gửi lỗi cho nhóm biên dịch

0

Tôi chỉ có một sự hiểu biết mơ hồ về các hoạt động chính xác của trình inferrer ở Scala vì vậy hãy xem xét ý tưởng này không phải là câu trả lời dứt khoát.

  1. Loại inferring có vấn đề với việc suy ra nhiều hơn một loại cùng một lúc.

  2. Bạn sử dụng một kiểu hiện sinh trong định nghĩa của FOO, tức đạt: có tồn tại một loại như vậy, không chắc chắn nếu điều này là phù hợp với loại hình cụ thể được đưa ra trong MyFoo

Các vấn đề liên quan