2013-04-17 39 views
7

Có bất kỳ lý do nào tại sao đặc điểm của Scala là Ordering không phải là contravariant? Một ví dụ thúc đẩy sau.Scala: Đặt hàng contravariance

Giả sử tôi muốn thực hiện chèn được sắp xếp. Tôi có thể có một chức năng với chữ ký

def insert[A, B >: A](list: List[A], item: A)(implicit ord: Ordering[B]): List[A] 

Ở đây, tôi có một Ordering mà chấp nhận loại siêu kiểu A. Tôi tưởng tượng điều này rất hữu ích khi bạn đang giao dịch với case classes. Ví dụ:

abstract class CodeTree 
case class Fork(left: CodeTree, right: CodeTree, chars: List[Char], weight: Int) extends CodeTree 
case class Leaf(char: Char, weight: Int) extends CodeTree 

def weight(tree: CodeTree): Int 
def chars(tree: CodeTree): List[Char] 

implicit object CodeTreeOrdering extends Ordering[CodeTree] { 
    def compare(a: CodeTree, b: CodeTree): Int = weight(a) compare weight(b) 
} 

tôi muốn chức năng chèn của tôi để làm việc với các loại List[CodeTree], List[Leaf] hoặc List[Fork]. Tuy nhiên, như Ordering không phải là contravariant, tôi cần phải xác định tiềm ẩn Orderings cho mỗi case.

Nếu tôi xác định

trait MyOrdering[-A] { 
    def compare(a: A, b: A): Int 
} 

mọi thứ hoạt động như mong đợi.

Có cách nào khác để đạt được mục tiêu của tôi không?

EDIT:

giải pháp hiện tại của tôi là xác định chèn như

def insert[A](list: List[A], item: A)(implicit ord: Ordering[A]): List[A] 

mà hoạt động tốt khi giao dịch với List[CodeTree]. Tôi cũng xác định (lấy cảm hứng từ thư viện scalaz):

trait Contravariant[F[_]] { 
    def contramap[A, B](r: F[A], f: B => A): F[B] 
} 

implicit object OrderingContravariant extends Contravariant[Ordering] { 
    def contramap[A, B](r: Ordering[A], f: B => A) = r.on(f) 
} 

implicit def orderingCodeTree[A <: CodeTree]: Ordering[A] = 
    implicitly[Contravariant[Ordering]].contramap(CodeTreeOrdering, identity) 

Tôi đang xác định hàm nhà máy ngầm cho Ordering[A <: CodeTree] trường hợp.

+2

Có vẻ như đó là một vấn đề kỹ thuật liên quan đến trình inferencer loại không tìm được thứ tự cụ thể nhất. Xem http://scala-programming-language.1934581.n4.nabble.com/Contravariant-Ordering-T-java-util-Comparator-and-uncheckedVariance-td1955224.html để biết chi tiết. – Impredicative

+1

@Impredicative Tôi đã chỉnh sửa bài đăng bằng cách giải quyết khó chịu. –

Trả lời

4

Một chút nhiều hơn một câu trả lời chi tiết rút khỏi thread trên 'scala ngôn ngữ' liên kết trong các bình luận trên.

Lý do mà Ordering không phải là contravariant là điều này không phù hợp hợp lý với khái niệm về độ đặc hiệu được sử dụng trong độ phân giải ngầm định. Độ phân giải ngầm định sẽ cố gắng chọn loại 'cụ thể' nhất cho tham số và xem xét một loại cụ thể hơn loại khác nếu đó là loại phụ của nó. Điều này có ý nghĩa trong trường hợp covariant: Tôi muốn có một tiềm ẩn cụ thể cho danh sách các chuỗi của tôi hơn một cho bất kỳ danh sách cũ. Trong trường hợp contravariant, tuy nhiên, nó muốn chọn điều sai:

trait Ord[-A] 
A <: B 
Ord[B] <: Ord[A] 

Và vì vậy nó sẽ chọn thứ tự 'cụ thể nhất' như là, nếu có, Ordering[Any].

Có vẻ như là một big discussion đang diễn ra thay đổi cách 'độ đặc hiệu' được xác định đối với các tham số contravariant trên nhóm ngôn ngữ scala.

+0

Ah, tôi thấy vấn đề. Tôi đã hy vọng ngày thứ hai của tôi với Scala sẽ mượt mà hơn! –

2

Trong API hiện nay, những phương pháp ngăn chặn nó là contravariant:

/** Return `x` if `x` >= `y`, otherwise `y`. */ 
    def max(x: T, y: T): T = if (gteq(x, y)) x else y 

    /** Return `x` if `x` <= `y`, otherwise `y`. */ 
    def min(x: T, y: T): T = if (lteq(x, y)) x else y 
+0

Thật dễ dàng để sửa 'max' và' min' bằng cách tham số trên một kiểu con 'T'. – Impredicative

+0

True - giải pháp đối số cụ thể nhất sẽ là câu trả lời chính xác nhất. – axel22