2012-03-07 34 views
5

Cho một tuple với các yếu tố của loại A và một loại parametrised trong A:Strange loại không phù hợp khi sử dụng truy cập thành viên thay vì vắt

trait Writer[-A] { def write(a: A): Unit } 
case class Write[A](value: A, writer: Writer[A]) 

Và một trang web sử dụng:

trait Cache { def store[A](value: A, writer: Writer[A]): Unit } 

Tại sao sau khi làm việc như mong đợi, sử dụng bộ chiết của tuple:

def test1(set: Set[Write[_]], cache: Cache): Unit = 
    set.foreach { 
    case Write(value, writer) => cache.store(value, writer) 
    } 

Nhưng thất bại sau:

def test2(set: Set[Write[_]], cache: Cache): Unit = 
    set.foreach { write => 
    cache.store(write.value, write.writer) 
    } 

với thông báo lỗi

found : Writer[_$1] where type _$1 
required: Writer[Any] 
      cache.store(write.value, write.writer) 
             ^

Tôi có thể sửa chữa các hình thức thứ hai (test2) để biên dịch đúng cách?

EDIT

Khởi hành từ những ý tưởng của Owen Tôi cố gắng hiểu xem tôi có thể làm cho nó làm việc mà không phù hợp với mô hình ở tất cả (đó là những gì tôi muốn ở nơi đầu tiên). Dưới đây là hai trường hợp kỳ lạ, người ta làm việc, người kia không:

// does not work 
def test3(set: Set[Write[_]], cache: Cache): Unit = { 
    def process[A](write: Write[A]): Unit = 
    cache.store(write.value, write.writer) 

    set.foreach(process) 
} 

// _does work_ 
def test4(set: Set[Write[_]], cache: Cache): Unit = { 
    def process[A](write: Write[A]): Unit = 
    cache.store(write.value, write.writer) 

    set.foreach(w => process(w)) 
} 

vẫn còn khá mơ hồ với tôi ...

Trả lời

7

Chạy với -Xprint:typer được chiếu sáng ở đây. Vấn đề với test2 là rằng có một loại hiện sinh, xuất hiện ở hai nơi riêng biệt: cả write.valuewrite.writer có một loại tồn tại, nhưng điều quan trọng là trình biên dịch không có cách nào để biết rằng họ có cùng existentially biến loại được định lượng. Mặc dù bạn truy cập chúng từ cùng một đối tượng, trình biên dịch quên chúng đến từ cùng một vị trí.

Khi test1 được gõ đầy đủ, bạn sẽ thấy:

def test1(set: Set[Write[_]], cache: Cache) = 
    set.foreach(((x0$1: Write[_]) => x0$1 match { 
     case (value: _$1, writer: Writer[_$1])Write[_$1]((value @ _), (writer @ _)) => 
      cache.store[_$1](value, writer) 
    })); 

Loại biến _$1 là lần xuất hiện cùng với các giá trị. Phù hợp với biến loại _$1 liên kết nó trong phạm vi của case, vì vậy nó không còn tồn tại nữa và Scala có thể cho biết rằng valuewriter có cùng thông số loại.

Giải pháp cho test2 là không sử dụng existentials:

def test2[A](set: Set[ Write[ A ]], cache: Cache) { 
    set.foreach { write => 
     cache.store(write.value, write.writer) 
    } 
} 

hoặc để ràng buộc các biến kiểu với một trận đấu:

def test2(set: Set[ Write[ _ ]], cache: Cache) { 
    set.foreach { case write: Write[a] => 
     cache.store(write.value, write.writer) 
    } 
} 

chỉnh sửa

Hãy để tôi cố gắng trả lời những câu hỏi mới bạn đã đưa ra.

Lý do test3 không hoạt động, đó là khi bạn viết:

set.foreach (quy trình)

process, đó là đa hình, đã được thực hiện monomorphic, vì hai lý do (mà tôi biết of):

  1. Chức năng trong Scala không thể đa hình; chỉ có phương pháp có thể được. process như được định nghĩa là phương thức; khi được sử dụng như một hàm hạng nhất, nó là một hàm.

  2. Cách trình biên dịch loại suy luận chủ yếu bằng cách lấy các giá trị đa hình và hợp nhất chúng lại với nhau để làm cho chúng ít đa hình. Việc chuyển giá trị đa hình thực tế làm đối số phương thức sẽ yêu cầu các loại xếp hạng cao hơn.

Lý do mà test4không công việc là chức năng theo nghĩa đen:

set.foreach(w => process(w)) 

là thực sự không phải là một chức năng đa hình! Nó lấy làm đối số của nó một loại có trình độ tiên tiến; nhưng không phải là loại đa hình. Sau đó, nó gọi phương thức (không phải chức năng) process và đối sánh với biến loại hiện tại với thông số loại process. Khá hoang dã, eh?

Bạn cũng có thể viết:

set.foreach(process(_)) 

đó, tạo ra một chức năng ẩn danh, có nghĩa là điều tương tự.

Một tuyến đường bạn có thể hoặc không thể tìm thấy phù hợp sẽ được loại bỏ loại hiện sinh và loại sử dụng thành viên:

trait Writable { 
    type A 
    val value: A 
    val writer: Writer[A] 
} 

case class Write[T](value: T, writer: Writer[ T ]) extends Writable { 
    type A = T 
} 

def test2(set: Set[Writable], cache: Cache) { 
    set.foreach { write => 
     cache.store(write.value, write.writer) 
    } 
} 

Đây Scala có thể thấy rằng write.valuewrite.writer có thông số tương tự loại bởi vì họ có cùng kiểu phụ thuộc đường dẫn.

+0

Cảm ơn bạn đã giải thích. Giải pháp đầu tiên của bạn không áp dụng được, bởi vì mỗi Write sẽ có một tham số kiểu khác nhau. Giải pháp thứ hai của bạn là khả thi. –

+0

Tôi vẫn không thực sự hiểu những gì đang xảy ra. Xem chỉnh sửa của tôi cho câu hỏi. Đó là lý do tại sao tôi muốn giữ cho câu hỏi mở thêm một chút. –

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