เหตุใดชุดที่ไม่เปลี่ยนรูปของ Scala จึงไม่เหมือนกันในประเภทของมัน?


94

แก้ไข : เขียนคำถามนี้ใหม่ตามคำตอบเดิม

scala.collection.immutable.Setชั้นไม่ได้ covariant ในพารามิเตอร์ชนิดของมัน ทำไมถึงเป็นแบบนี้?

import scala.collection.immutable._

def foo(s: Set[CharSequence]): Unit = {
    println(s)
}

def bar(): Unit = {
   val s: Set[String] = Set("Hello", "World");
   foo(s); //DOES NOT COMPILE, regardless of whether type is declared 
           //explicitly in the val s declaration
}

เป็นที่น่าสังเกตว่าfoo(s.toSet[CharSequence])รวบรวมได้ดี toSetวิธีเป็น O (1) - asInstanceOfมันเป็นเพียงแค่การตัด
john sullivan

1
โปรดทราบว่าfoo(Set("Hello", "World"))คอมไพล์ด้วย 2.10 เนื่องจาก Scala ดูเหมือนจะสามารถอนุมานประเภทที่ถูกต้องของ Set มันใช้ไม่ได้กับการแปลงโดยนัยแม้ว่า ( stackoverflow.com/questions/23274033/… )
LP_

คำตอบ:


55

Setไม่แปรผันในพารามิเตอร์ type เนื่องจากแนวคิดเบื้องหลังชุดเป็นฟังก์ชัน ลายเซ็นต่อไปนี้ควรชี้แจงสิ่งต่างๆเล็กน้อย:

trait Set[A] extends (A=>Boolean) {
  def apply(e: A): Boolean
}

หากSetเป็นโรคโควาเรีAapplyวิธีนี้จะไม่สามารถใช้พารามิเตอร์ประเภทได้Aเนื่องจากฟังก์ชันที่ไม่ตรงกัน Setอาจจะเป็นcontravariantในAแต่เรื่องนี้ทำให้เกิดปัญหามากเกินไปเมื่อคุณต้องการที่จะทำสิ่งเช่นนี้

def elements: Iterable[A]

ในระยะสั้นวิธีแก้ปัญหาที่ดีที่สุดคือทำให้สิ่งต่าง ๆ ไม่เปลี่ยนแปลงแม้โครงสร้างข้อมูลที่ไม่เปลี่ยนรูป คุณจะสังเกตเห็นว่าimmutable.Mapพารามิเตอร์ประเภทหนึ่งไม่แปรผันเช่นกัน


4
ฉันเดาว่าข้อโต้แย้งนี้เกี่ยวกับ "แนวคิดเบื้องหลังชุดเป็นฟังก์ชัน" - สิ่งนี้สามารถขยายได้หรือไม่? ตัวอย่างเช่น "set as a function" มีข้อดีอะไรบ้างที่ทำให้ "set as a collection" ไม่ได้ คุ้มไหมที่จะเสียการใช้โควาเรียนประเภทนั้น?
oxbow_lakes

23
ลายเซ็นประเภทเป็นตัวอย่างที่ค่อนข้างอ่อนแอ "นำไปใช้" ของเซตคือสิ่งเดียวกับที่มีเมธอด อนิจจารายการของ Scala เป็นตัวแปรร่วมและมีวิธีการด้วยเช่นกัน ลายเซ็นของ List มีความแตกต่างกันแน่นอน แต่วิธีการทำงานเหมือนกับ Set's ดังนั้นจึงไม่มีอะไรหยุด Set จากการเป็นตัวแปรร่วมยกเว้นการตัดสินใจในการออกแบบ
Daniel C. Sobral

6
ชุดไม่ใช่ฟังก์ชันบูลีนจากมุมมองทางคณิตศาสตร์ ชุดต่างๆ "สร้างขึ้น" จากสัจพจน์ของ Zermelo-Fraenkel ซึ่งไม่ได้ลดลงด้วยฟังก์ชันการรวมบางอย่าง เหตุผลเบื้องหลังเรื่องนี้คือความขัดแย้งของรัสเซล: ถ้าสิ่งใดสามารถเป็นสมาชิกของเซตได้ให้พิจารณาเซต R ของเซตที่ไม่ใช่สมาชิกของตัวเอง แล้วถามคำถามว่า R เป็นสมาชิกของ R หรือไม่?
oxbow_lakes

12
ฉันยังไม่มั่นใจว่าการเสียสละความแปรปรวนร่วมนั้นคุ้มค่าสำหรับ Set แน่นอนว่าเป็นเรื่องดีที่เป็นเพรดิเคต แต่โดยปกติคุณสามารถใช้คำที่ละเอียดกว่านี้ได้เล็กน้อยและใช้ "set.contains" แทนที่จะเป็น "set" (และเนื้อหา "set.contains" จะอ่านได้ดีกว่าในหลาย ๆ กรณี)
Matt R

4
@ Martin: เพราะรายการของมีวิธีการใช้เวลาใด ๆ ที่ไม่ A. ประเภทของList(1,2,3).contains _คือ(Any) => Booleanในขณะที่ประเภทของการเป็นSet(1,2,3).contains _ res1: (Int) => Boolean
Seth Tisue

52

ที่http://www.scala-lang.org/node/9764 Martin Odersky เขียน:

"ในเรื่องของเซตฉันเชื่อว่าความไม่แปรปรวนเกิดจากการนำไปใช้เช่นกันชุดทั่วไปถูกนำมาใช้เป็นแฮชแท็กซึ่งไม่ใช่อาร์เรย์ประเภทคีย์ที่ไม่แปรผันฉันยอมรับว่าเป็นความผิดปกติที่น่ารำคาญเล็กน้อย"

ดังนั้นดูเหมือนว่าความพยายามทั้งหมดของเราในการสร้างเหตุผลที่เป็นหลักการสำหรับสิ่งนี้ถูกเข้าใจผิด :-)


1
แต่บางลำดับยังใช้กับอาร์เรย์และยังคงSeqเป็นโควาเรีย ... ฉันขาดอะไรไปหรือเปล่า?
LP_

4
สิ่งนี้สามารถแก้ไขได้เล็กน้อยโดยการจัดเก็บArray[Any]ภายใน
rightfold

@rightfold ถูกต้อง อาจมีเหตุผลที่สมเหตุสมผล แต่นี่ไม่ใช่
Paul Draper

6

แก้ไข : สำหรับใครที่สงสัยว่าทำไมคำตอบนี้ดูไม่ตรงประเด็นเล็กน้อยนั่นเป็นเพราะฉัน (ผู้ถาม) ได้แก้ไขคำถาม

การอนุมานประเภทของ Scala นั้นดีพอที่จะเข้าใจว่าคุณต้องการ CharSequences ไม่ใช่ Strings ในบางสถานการณ์ โดยเฉพาะอย่างยิ่งสิ่งต่อไปนี้ใช้ได้กับฉันใน 2.7.3:

import scala.collections.immutable._
def findCharSequences(): Set[CharSequence] = Set("Hello", "World")

วิธีสร้างไม่เปลี่ยนรูปแฮชเซ็ตโดยตรง: อย่า เนื่องจากการเพิ่มประสิทธิภาพการใช้งานไม่เปลี่ยนรูปแฮชเซ็ตที่มีองค์ประกอบน้อยกว่า 5 องค์ประกอบไม่ใช่อินสแตนซ์ของการเปลี่ยนรูปไม่ได้จริงๆ มีทั้ง EmptySet, Set1, Set2, Set3 หรือ Set4 คลาสย่อยเหล่านี้ไม่เปลี่ยนรูปตั้งค่า แต่ไม่เปลี่ยนรูป HashSet


คุณพูดถูก; ในการพยายามทำให้ตัวอย่างจริงของฉันง่ายขึ้นฉันได้ทำผิดพลาดเล็กน้อย :-(
oxbow_lakes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.