2013-01-06 17 views
9

Tôi đang cố gắng viết một số phương pháp mở rộng cho các bộ sưu tập Scala, và gặp rắc rối khi phát huy chúng hoàn toàn.Làm cách nào để sử dụng loại thành viên A trong IsTraversableLike?

Một nỗ lực đầu tiên tại tailOption mang lại một cái gì đó như:

implicit class TailOption[A, Repr <: GenTraversableLike[A, Repr]](val repr: Repr) { 
    def tailOption: Option[Repr] = 
    if (repr.isEmpty) None 
    else Some(repr.tail) 
} 

không may, điều này không làm việc:

scala> List(1,2,3).tailOption 
<console>:19: error: value tailOption is not a member of List[Int] 
       List(1,2,3).tailOption 

Scala 2.10 cung cấp IsTraversableLike kiểu lớp để giúp thích nghi với các loại điều này cho tất cả các bộ sưu tập (kể cả các bộ sưu tập lẻ, chẳng hạn như Strings).

Với điều này tôi có thể ví dụ như thực hiện tailOption khá dễ dàng:

implicit class TailOption[Repr](val r: Repr)(implicit fr: IsTraversableLike[Repr]) { 
    def tailOption: Option[Repr] = { 
    val repr = fr.conversion(r) 
    if (repr.isEmpty) None 
    else Some(repr.tail) 
    } 
} 

scala> List(1,2,3).tailOption 
res12: Option[List[Int]] = Some(List(2, 3)) 

scala> "one".tailOption 
res13: Option[String] = Some(ne) 

Kết quả là của đúng loại: Option[<input-type>]. Cụ thể, tôi đã có thể bảo toàn loại Repr khi gọi các phương thức trả lại Repr, như `đuôi.

Thật không may, tôi dường như không thể sử dụng mẹo này để giữ nguyên loại yếu tố của bộ sưu tập. Tôi không thể gọi các phương thức trả về một phần tử.

IsTraversableLike không có thành viên A nhưng có vẻ như không hữu ích. Đặc biệt, tôi không thể tái tạo lại kiểu phần tử gốc của mình và thành viên không tương đương với kiểu. Ví dụ, không có việc làm thêm, headTailOption trông như thế này:

implicit class HeadTailOption[Repr](val r: Repr)(implicit val fr: IsTraversableLike[Repr]) { 
    def headTailOption: Option[(fr.A, Repr)] = { 
    val repr = fr.conversion(r) 
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail) 
    } 
} 

scala> val Some((c, _)) = "one".headTailOption 
c: _1.fr.A forSome { val _1: HeadTailOption[String] } = o 

Như chúng ta có thể thấy, c có một loại tuyệt vời baroque. Nhưng, loại này là không tương đương với Char:

scala> val fr = implicitly[IsTraversableLike[String]] 
fr: scala.collection.generic.IsTraversableLike[String] = [email protected] 

scala> implicitly[fr.A <:< Char] 
<console>:25: error: Cannot prove that fr.A <:< Char. 
       implicitly[fr.A <:< Char] 

Tôi đã thử tất cả các loại thủ đoạn kể cả có Repr[A] <: GenTraversableLike[A, Repr[A]] không ai trong số đó giúp đỡ. Bất cứ ai có thể làm ra nước sốt ma thuật để làm cho headTailOption trả lại đúng loại cho:

val headTailString: Option[(Char, String)] = "one".headTailOption 
val headTailList: Option[(Int, List[Int])] = List(1,2,3).headTailOption 

Trả lời

3

Câu trả lời một phần. Có thể bạn đã bắt đầu từ ví dụ về scaladoc cho IsTraversableLike. Nó vẫn sử dụng "cách tiếp cận cũ" của việc tách biệt chuyển đổi ngầm định và khởi tạo lớp trình bao bọc, thay vì đi qua một bước thông qua một lớp ngầm định. Nó chỉ ra rằng các "phương pháp cũ" làm việc:

import collection.GenTraversableLike 
import collection.generic.IsTraversableLike 

final class HeadTailOptionImpl[A, Repr](repr: GenTraversableLike[A, Repr]) { 
    def headTailOption: Option[(A, Repr)] = { 
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail) 
    } 
} 

implicit def headTailOption[Repr](r: Repr)(implicit fr: IsTraversableLike[Repr]): 
    HeadTailOptionImpl[fr.A,Repr] = new HeadTailOptionImpl(fr.conversion(r)) 

// `c` looks still weird: `scala.collection.generic.IsTraversableLike.stringRepr.A` 
val Some((c, _)) = "one".headTailOption 
val d: Char = c // ...but it really is a `Char`! 

val headTailString: Option[(Char, String)] = "one".headTailOption 
val headTailList: Option[(Int, List[Int])] = List(1,2,3).headTailOption 

Như Miles chỉ ra, sự chia rẽ dường như cần thiết để làm việc này với việc tìm kiếm và suy luận kiểu ngầm.

Một giải pháp khác, mặc dù tất nhiên ít thanh lịch, là từ bỏ sự thống nhất của chuỗi và bộ sưu tập:

trait HeadTailOptionLike[A, Repr] { 
    def headTailOption: Option[(A, Repr)] 
} 

implicit class GenHeadTailOption[A, Repr](repr: GenTraversableLike[A, Repr]) 
extends HeadTailOptionLike[A, Repr] { 
    def headTailOption = 
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail) 
} 

implicit class StringHeadTailOption(repr: String) 
extends HeadTailOptionLike[Char, String] { 
    def headTailOption = 
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail) // could use repr.charAt(0) -> repr.substring(1) 
} 

List(1,2,3).headTailOption 
"one".headTailOption 
+0

Bạn đã đánh bại tôi. Thật không may là sự phân chia giữa phương thức ngầm sử dụng kiểu phụ thuộc fr.A và lớp tham số kiểu là cần thiết ở đây, vì vậy tôi không tin rằng một giải pháp lớp ngầm định là (hiện tại) có thể. –

+0

Vâng, tôi đã đi đến cùng một kết luận ngay bây giờ. Lớp ngầm định không có khả năng tái tạo kiểu chuyển đổi/suy luận kiểu cần thiết của kiểu phụ thuộc ... –

0

Bạn có thể viết nó như một lớp tiềm ẩn nếu bạn trích xuất các loại A từ IsTraversableLike.

import collection.generic.IsTraversableLike 

implicit class HeadTailOption[Repr,A0](val r: Repr)(implicit val fr: IsTraversableLike[Repr]{ type A = A0 }) { 
    def headTailOption: Option[(A0, Repr)] = { 
    val repr = fr.conversion(r) 
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail) 
    } 
} 

Hoặc tương đương:

import collection.generic.IsTraversableLike 

type IsTraversableLikeAux[Repr,A0] = IsTraversableLike[Repr]{ type A = A0 } 

implicit class HeadTailOption[Repr,A](val r: Repr)(implicit val fr: IsTraversableLikeAux[Repr,A]) { 
    def headTailOption: Option[(A, Repr)] = { 
    val repr = fr.conversion(r) 
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail) 
    } 
} 

Và sau đó tất cả mọi thứ hoạt động tốt.

scala> val Some((c, _)) = "one".headTailOption 
c: scala.collection.generic.IsTraversableLike.stringRepr.A = o 

scala> c.isSpaceChar 
res0: Boolean = false 

Trình biên dịch biết rằng scala.collection.generic.IsTraversableLike.stringRepr.A giống như Char.

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