2012-01-03 20 views
13

Có bất kỳ lý trí nào cho Option không phải là Traversable không?Tại sao Tùy chọn không thể chuyển giao?

Trong Scala 2.9, Seq(Set(1,3,2),Seq(4),Option(5)).flatten không biên dịch và chỉ đơn giản là có nó để thực hiện các đường nối đặc điểm Traversable hợp lý với tôi. Nếu không phải vậy, chắc chắn tôi không thấy điều đó. Nó là gì?

PS: Trong khi cố gắng tìm hiểu, tôi đạt được những điều khủng khiếp mà biên dịch, như:

scala> Seq(Set(1,3,2),Seq(4),Map("one"->1, 2->"two")).flatten 
res1: Seq[Any] = List(1, 3, 2, 4, (one,1), (2,two)) 

PS2: Tôi biết tôi có thể viết: Seq(Set(1,3,2),Seq(4),Option(5).toSeq).flatten hoặc khác điều xấu xí.

PS3: Có vỉa được làm việc trong tháng vừa qua để làm Option nhìn giống như Traversable mà không thực hiện nó: commit, another commit

+2

Đối với các câu hỏi thiết kế về ngôn ngữ (hoặc trong trường hợp này) như thế này, tốt nhất nên hỏi trực tiếp nhà thiết kế ngôn ngữ (hoặc thư viện). Họ khá nhạy cảm trên danh sách gửi thư nhưng thỉnh thoảng lại ghé thăm StackOverflow. –

+4

@ JörgWMittag Thật tuyệt khi có câu trả lời được biên soạn trên SO, mặc dù .. –

Trả lời

5

Có thể có những thách thức khi có flatMap trả về số Option thay vì Traversable. Mặc dù có trước toàn bộ máy móc 2.8 CanBuildFrom.

Câu hỏi là asked một lần trước trong danh sách gửi thư nhưng không gợi ra phản hồi.

Dưới đây là một ví dụ:

sealed trait OptionX[+A] extends Traversable[A] { 
    def foreach[U](f: (A) => U): Unit = if (!isEmpty) f(get) 
    def get: A 
    def isDefined: Boolean 
    def getOrElse[B >: A](default: => B): B 
} 

case class SomeX[+A](a: A) extends OptionX[A] { 
    override def isEmpty = false 
    def get = a 
    def isDefined = true 
    def getOrElse[B >: A](default: => B) = a 
} 

case object NoneX extends OptionX[Nothing] { 
    override def isEmpty = true 
    def get = sys.error("none") 
    def isDefined = false 
    def getOrElse[B](default: => B) = default 
} 

object O extends App { 
    val s: OptionX[Int] = SomeX(1) 
    val n: OptionX[Int] = NoneX 
    s.foreach(i => println("some " + i)) 
    n.foreach(i => println("should not print " + i)) 
    println(s.map(_ + "!")) 
} 

Dòng cuối cùng trả về một List("1!") thay vì Option. Có thể ai đó có thể đưa ra một CanBuildFrom có thể mang lại một SomeX("1!"). nỗ lực của tôi đã không thành công:

object OptionX { 
    implicit def canBuildFrom[Elem] = new CanBuildFrom[Traversable[_], Elem, OptionX[Elem]] { 
    def builder() = new Builder[Elem, OptionX[Elem]] { 
     var current: OptionX[Elem] = NoneX 
     def +=(elem: Elem): this.type = { 
     if (current.isDefined) sys.error("already defined") 
     else current = SomeX(elem) 
     this 
     } 
     def clear() { current = NoneX } 
     def result(): OptionX[Elem] = current 
    } 
    def apply() = builder() 
    def apply(from: Traversable[_]) = builder() 
    } 
} 

tôi cần phải vượt qua ngầm một cách rõ ràng:

scala> import o._ 
import o._ 

scala> val s: OptionX[Int] = SomeX(1) 
s: o.OptionX[Int] = SomeX(1) 

scala> s.map(_+1)(OptionX.canBuildFrom[Int]) 
res1: o.OptionX[Int] = SomeX(2) 

scala> s.map(_+1) 
res2: Traversable[Int] = List(2) 

Edit:

Vì vậy, tôi đã có thể làm việc xung quanh vấn đề này và có SomeX(1).map(1+) trở lại một OptionX bằng cách có OptionX mở rộng TraversableLike[A, OptionX[A]] và ghi đè newBuilder.

Nhưng sau đó tôi gặp lỗi thời gian chạy trên SomeX(1) ++ SomeX(2) hoặc for (i <- SomeX(1); j <- List(1,2)) yield (i+j). Vì vậy, tôi không nghĩ rằng nó có thể có tùy chọn mở rộng Traversable và làm điều gì đó lành mạnh về việc trả lại loại cụ thể nhất.

Ngoài tính khả thi, kiểu mã hóa khôn ngoan, tôi không chắc đó là một điều tốt để có Option hoạt động như một Traversable trong mọi trường hợp. Option đại diện cho các giá trị không phải lúc nào cũng được xác định, trong khi Traversable xác định các phương thức cho các bộ sưu tập có thể có nhiều phần tử trong đó như drop(n), splitAt(n), take(n), ++. Mặc dù nó sẽ cung cấp sự tiện lợi nếu Option cũng là một Traversable, tôi nghĩ rằng nó có thể làm cho ý định ít rõ ràng hơn.

Sử dụng toSeq khi cần thiết có vẻ như một cách không đau để cho biết rằng tôi muốn tùy chọn hoạt động của tôi giống như Traversable. Và đối với một số trường hợp sử dụng lặp đi lặp lại, có option2Iterable chuyển đổi ngầm - vì vậy cho trường hợp này đã làm việc (tất cả họ đều trở List(1,2)):

  • List(Option(1), Option(2), None).flatten
  • for (i <- List(0,1); j <- Some(1)) yield (i+j)
  • Some(1) ++ Some(2)
+0

Câu trả lời hay, cảm ơn – shellholic

1

Lý do là trong một số trường hợp với implicits áp dụng loại sẽ nhận được ít chính xác. Bạn vẫn sẽ có giá trị Option, nhưng kiểu trả về tĩnh sẽ là một cái gì đó như Iterable, e. g. không phải là "chính xác nhất".

1

Có lẽ tôi 's là dày đặc, nhưng tôi không hiểu tại sao bất cứ ai sẽ cần điều này. Hơn nữa, nó sẽ yêu cầu None để được Traversable cũng như có vẻ nghi ngờ ngữ nghĩa.

Họ nói rằng thiết kế hoàn tất không phải khi không còn gì để thêm, nhưng không có gì còn lại để lấy đi. Mà không phải là để nói, tất nhiên, rằng thư viện chuẩn Scala là hoàn hảo.

+1

'Nil' là' Traversable' – shellholic

+0

Touché. Tuy nhiên, 'Nil' và' None' có ý nghĩa khá khác nhau. 'Nil' mở rộng' List [Nothing] 'và là giá trị của' List() '. Nếu 'Nil' không phải là' Traversable', nó sẽ gây ra rất nhiều vấn đề :) –

+0

Làm thế nào về 'Future.traverse (option) (f)' hoặc 'Future.sequence (option)'. Tôi nghĩ rằng bạn có thể bình đẳng cho rằng trích dẫn từ Antoine de Saint-Exupéry là một đối số ủng hộ 'Tùy chọn' là' Traversable'. Ngay bây giờ chúng ta có "một Traversable' là một thứ mà bạn có thể đi qua, ngoại trừ' Option' có gần như tất cả hành vi của một 'Traversable' nhưng đó không phải là một vì vậy bạn nên có các phương thức riêng biệt cho' Tùy chọn' và mọi loại bộ sưu tập khác. " Điều chúng tôi đã thêm vào là một sự phân biệt tùy ý không tồn tại một cách hợp lý. –

4

Nó không phải là Traversable vì bạn không thể triển khai scala.collection.mutable.Builder cho nó.

Vâng, nó có thể là Traversable ngay cả như vậy, nhưng điều đó sẽ dẫn đến nhiều phương thức trả lại Option hiện trả lại Traversable thay thế. Nếu bạn muốn xem những phương thức này là gì, chỉ cần nhìn vào các phương thức có tham số CanBuildFrom.

Chúng ta hãy mẫu mã của bạn để chứng minh lý do tại sao:

Seq(Set(1,3,2),Seq(4),Option(5)).flatten 

Đó nên được bằng:

Seq(1, 2, 3, 4, 5) 

Bây giờ, chúng ta hãy xem xét thay thế này:

Option(Set(1,3,2),Seq(4),Option(5)).flatten 

gì là giá trị của điều đó?

+2

'Tùy chọn # apply' có một tham số và câu trả lời trong trường hợp đó là:' Option (Set (1,3,2)). Flatten => Set (1,3,2) ' – shellholic

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