2015-02-21 20 views
8

Các đoạn mã sau đây không biên dịch:Scala suy luận kiểu với nhiều loại hiện sinh và loại thành viên

trait A[F] { 
    def find(x: Int): F 
    def fill(f: F): Unit 
} 

object TestA { 
    def test[T <: A[F] forSome { type F }](t: T) = 
    t.fill(t.find(0)) 
} 

Nó trả về lỗi biên dịch sau:

test.scala:8: error: type mismatch; 
found : (some other)F(in type T) 
required: F(in type T) 
    t.fill(t.find(0)) 
       ^

Tuy nhiên đoạn mã sau tuân thủ tốt:

trait B[F] { 
    type R = F 
    def find(x: Int): R 
    def fill(f: R): Unit 
} 

object TestB { 
    def test[T <: B[F] forSome { type F }](t: T) = 
    t.fill(t.find(0)) 
} 

Tôi có hai câu hỏi ở đây:

  1. Tôi mong đợi đoạn mã nắm tay để biên dịch. Tại sao nó không?

  2. Nếu có một lý do chính đáng tại sao mảnh đầu tiên của mã không biên dịch, tôi mong chờ thứ hai để không biên dịch, hoặc, với lý do tương tự. Làm thế nào sau đó, nó biên dịch thành công?

Một trong hai lỗi này là gì?

Trả lời

3

Tôi không biết tại sao trình biên dịch phân biệt hai phần mã. Về cơ bản, mã không biên dịch vì loại được trả về bởi find và loại được mong đợi bởi fill không phải giống nhau F, ít nhất nếu findfill được gọi trên hai đối tượng khác nhau.

Bạn có thể làm cho phần đầu tiên của mã để biên dịch với:

def test[T <: A[F], F](t: T) = 
    t.fill(t.find(0)) 

Và bạn có thể làm cho mảnh thứ hai của mã không biên dịch với:

def test[T <: B[F] forSome { type F }](t: T, u: T) = 
    t.fill(u.find(0)) 

này nên thay vì một lời nhận xét hơn là một câu trả lời, nhưng tôi chưa có 50 danh tiếng.

2

Để hiểu điều gì đang xảy ra, hãy xem các phiên bản đơn giản của TestA.testTestB.test.

object TestA { 
    def test1(a: A[String]) = { 
    val s: String = a.find(0) 
    a.fill(s) 
    } 
} 

object TestB { 
    def test1(b: B[String]) = { 
    val r: b.R = b.find(0) 
    b.fill(r) 
    } 
} 

Chú ý cách loại giá trị trung gian s đề cập đến String, trong khi các loại giá trị trung gian r không.

object TestA { 
    def test2(a: A[F] forSome {type F}) = { 
    val s: F forSome {type F} = a.find(0) 

    // type mismatch; 
    // found : s.type (with underlying type F forSome { type F }) 
    // required: F 
    a.fill(s) 
    } 
} 

object TestB { 
    def test2(b: B[F] forSome {type F}) = { 
    val r: b.R = b.find(0) 
    b.fill(r) 
    } 
} 

Một khi chúng ta vứt trong existentials, chúng tôi kết thúc với một giá trị trung gian s có loại tương đương với Any, và do đó mà không phải là một đầu vào hợp lệ cho a.fill. Tuy nhiên, loại trung gian cho r vẫn là b.R và do đó, nó vẫn là đầu vào thích hợp cho b.fill. Lý do loại của nó vẫn còn là b.R là vì b.R không đề cập đến F, và vì vậy theo the simplification rules for existential types, b.R forSome {type F} tương đương với b.R, trong cùng một cách mà Int forSome {type F} tương đương với Int.

Một trong hai lỗi này là gì?

Vâng, chắc chắn có lỗi ở đâu đó (như của scalac 2.11.7), vì sau đây không nhập kiểm tra.

object TestB { 
    def test3(b: B[F] forSome {type F}) = { 
    val r: b.R forSome {type F} = b.find(0) 

    // type mismatch; 
    // found : F 
    // required: b.R 
    //  (which expands to) F 
    b.fill(r) 
    } 
} 

Vì vậy, hoặc tôi là sai lầm khi nghĩ rằng b.R không đề cập đến F, trong trường hợp b.R forSome {type F} không tương đương với b.RTestB.test bạn không nên gõ kiểm tra nhưng nó, hoặc b.R forSome {type F} được equivalalent để b.R, trong trường hợp này, TestB.test3 của tôi nên nhập séc nhưng không.

Tôi khá thuyết phục rằng lỗi là với sau này, bởi vì lỗi thậm chí xảy ra khi định lượng hiện tại không có gì để làm với b.R, như trong ví dụ sau.

object TestB { 
    def test4(b: B[F] forSome {type F}) = { 
    val r: b.R forSome {val x: Int} = b.find(0) 

    // type mismatch; 
    // found : F 
    // required: b.R 
    //  (which expands to) F 
    b.fill(r) 
    } 
} 
Các vấn đề liên quan