<: <, <% <, และ =: = หมายถึงอะไรใน Scala 2.8 และเอกสารเหล่านั้นอยู่ที่ไหน


201

ฉันเห็นได้ในเอกสาร API สำหรับPredefว่าเป็นประเภทย่อยของประเภทฟังก์ชันทั่วไป (จาก) => ถึง แต่นั่นคือทั้งหมดที่กล่าว เอ่ออะไรนะ? อาจมีเอกสารอยู่บ้าง แต่เครื่องมือค้นหาไม่จัดการ "ชื่อ" เช่น "<: <" ดีมากดังนั้นฉันจึงไม่สามารถค้นหาได้

คำถามติดตามผล: เมื่อใดที่ฉันควรใช้สัญลักษณ์ / คลาสขี้ขลาดเหล่านี้และทำไม


6
นี่คือคำถามที่เกี่ยวข้องซึ่งอาจตอบคำถามของคุณอย่างน้อยบางส่วน: stackoverflow.com/questions/2603003/operator-in-scala
Yardena

13
symbolhound.comคือเพื่อนที่ใช้ค้นหาโค้ดของคุณ :)
ron

typeclassES Haskell ปฏิบัติงานกับผู้ปฏิบัติงานเหล่านี้หรือไม่? ตัวอย่าง: compare :: Ord a => a -> a -> Ordering? ฉันพยายามที่จะเข้าใจแนวคิดสกาล่านี้ด้วยความเคารพต่อส่วนของ Haskell
เควินเมเรดิ ธ

คำตอบ:


217

เหล่านี้เรียกว่าทั่วไป จำกัด พวกเขาอนุญาตให้คุณจากภายในคลาสที่กำหนดพารามิเตอร์หรือลักษณะเฉพาะเพื่อจำกัดพารามิเตอร์ประเภทใดประเภทหนึ่ง นี่คือตัวอย่าง:

case class Foo[A](a:A) { // 'A' can be substituted with any type
    // getStringLength can only be used if this is a Foo[String]
    def getStringLength(implicit evidence: A =:= String) = a.length
}

อาร์กิวเมนต์นัยevidenceที่จัดจำหน่ายโดยคอมไพเลอร์, IFF คือA Stringคุณอาจจะคิดว่ามันเป็นหลักฐานที่AจะStringโต้แย้ง --the ตัวเองไม่ได้มีความสำคัญเพียงรู้ว่ามันมีอยู่ [แก้ไข: ดีจริง ๆ แล้วมันมีความสำคัญทางเทคนิคเพราะมันหมายถึงการแปลงโดยปริยายจากAเป็นStringซึ่งเป็นสิ่งที่ช่วยให้คุณสามารถโทรa.lengthและไม่มีคอมไพล์ตะโกนใส่คุณ]

ตอนนี้ฉันสามารถใช้มันอย่างนั้น:

scala> Foo("blah").getStringLength
res6: Int = 4

แต่ถ้าฉันลองใช้กับFooสิ่งที่บรรจุสิ่งอื่นที่ไม่ใช่String:

scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]

คุณสามารถอ่านข้อผิดพลาดว่า "ไม่สามารถหาหลักฐานว่า Int == String" ... นั่นเป็นไปตามที่ควรจะเป็น! getStringLengthกำลังกำหนดข้อ จำกัด เพิ่มเติมเกี่ยวกับประเภทของAสิ่งที่เกินความFooต้องการโดยทั่วไป คือคุณจะสามารถเรียกใช้บนgetStringLength Foo[String]ข้อ จำกัด นี้มีการบังคับใช้ในเวลารวบรวมซึ่งยอดเยี่ยม!

<:<และ<%<ทำงานในทำนองเดียวกัน แต่มีการเปลี่ยนแปลงเล็กน้อย:

  • A =:= B หมายถึง A จะต้องเป็น B อย่างแน่นอน
  • A <:< Bหมายถึง A จะต้องเป็นชนิดย่อยของ B (คล้ายกับข้อ จำกัด ประเภทแบบง่าย<: )
  • A <%< Bหมายถึง A ต้องสามารถดูได้เป็น B อาจผ่านการแปลงแบบนัย (คล้ายกับข้อ จำกัด ประเภทแบบง่าย<%)

ตัวอย่างนี้โดย @retronym เป็นคำอธิบายที่ดีเกี่ยวกับวิธีการเรียงลำดับของสิ่งที่เคยประสบความสำเร็จและข้อ จำกัด ประเภททั่วไปทำให้ง่ายขึ้นในขณะนี้

ภาคผนวก

ในการตอบคำถามติดตามผลของคุณตัวอย่างที่ฉันให้นั้นเป็นสิ่งที่ถูกจัดทำขึ้นและไม่มีประโยชน์ชัดเจน แต่ลองจินตนาการว่าใช้มันเพื่อนิยามบางอย่างเช่นList.sumIntsเมธอดซึ่งรวมรายการของจำนวนเต็ม คุณไม่ต้องการที่จะอนุญาตให้วิธีนี้จะเรียกในเก่า ๆเพียงList List[Int]อย่างไรก็ตามตัวListสร้างประเภทไม่สามารถถูก จำกัด ได้ คุณยังต้องการที่จะมีรายการของสตริง foos บาร์และ whatnots ดังนั้นโดยการวางข้อ จำกัด ประเภททั่วไปบนsumIntsคุณสามารถมั่นใจได้ว่าเพียงแค่ว่าวิธีมีข้อ จำกัด List[Int]เพิ่มเติมว่ามันสามารถนำมาใช้ใน โดยพื้นฐานแล้วคุณกำลังเขียนรหัสตัวพิมพ์พิเศษสำหรับรายการบางประเภท


3
โอเค แต่ยังมีวิธีการโดยใช้ชื่อเดียวกันกับManifestที่คุณไม่ได้พูดถึง
Daniel C. Sobral

3
วิธีการManifestเป็น<:<และ>:>เพียงอย่างเดียว ... เนื่องจาก OP กล่าวถึงข้อ จำกัด ประเภททั่วไป 3 ประเภทอย่างแน่นอนฉันจึงคิดว่านั่นคือสิ่งที่เขาสนใจ
Tom Crockett

12
@IttayD: มันฉลาดสวย ... class =:=[From, To] extends From => Toซึ่งหมายความว่าค่าโดยนัยของประเภทFrom =:= Toเป็นจริงโดยปริยายแปลงจากไปFrom Toดังนั้นโดยการยอมรับพารามิเตอร์โดยนัยของประเภทA =:= Stringที่คุณกำลังจะบอกว่าสามารถแปลงไปโดยปริยายA Stringหากคุณเปลี่ยนแปลงคำสั่งและทำให้อาร์กิวเมนต์นัยเป็นชนิดString =:= Aก็จะไม่ทำงานเพราะนี้จะเป็นแปลงนัยจากการString A
Tom Crockett

25
สัญลักษณ์สามตัวนั้นมีชื่อหรือไม่? ปัญหาของฉันเกี่ยวกับสัญลักษณ์ซุปของสกาล่าก็คือพวกเขายากที่จะพูดคุยด้วยวาจาและแทบจะเป็นไปไม่ได้เลยที่จะใช้ Google หรือเครื่องมือค้นหาอื่น ๆ เพื่อค้นหาการสนทนาและตัวอย่างของการใช้งาน
Gigatron

4
@Andrea Nope สิ่งนี้จะใช้ได้ก็ต่อเมื่อประเภทเท่ากันทุกประการ โปรดทราบว่าฉันบอกว่าการมีค่าประเภทโดยนัยFrom =:= Toในขอบเขตนั้นหมายความว่าคุณมีการแปลงโดยนัยFrom => Toแต่ความหมายนั้นไม่ได้ทำงานไปข้างหลัง มีการแปลงนัยA => Bไม่ได้A =:= Bบ่งบอกถึงคุณมีตัวอย่างของ =:=เป็นปิดผนึกระดับนามธรรมที่กำหนดไว้ในและมีเพียงหนึ่งตัวอย่างเปิดเผยต่อสาธารณชนซึ่งเป็นนัยและเป็นประเภทscala.Predef A =:= Aดังนั้นคุณจะรับประกันว่าคุ้มค่าโดยนัยของประเภทA =:= Bพยานความจริงที่ว่าAและBมีค่าเท่ากัน
Tom Crockett

55

ไม่ใช่คำตอบที่สมบูรณ์ (คนอื่นตอบคำถามนี้ไปแล้ว) ฉันแค่ต้องการจดบันทึกสิ่งต่อไปนี้ซึ่งอาจช่วยให้เข้าใจไวยากรณ์ได้ดีขึ้น: วิธีที่คุณใช้ "โอเปอเรเตอร์" เหล่านี้ตามปกติตัวอย่างเช่นในตัวอย่างของ pelotom:

def getStringLength(implicit evidence: A =:= String)

ทำให้การใช้ทางเลือกที่สกาล่าของไวยากรณ์มัดสำหรับผู้ประกอบการประเภท

ดังนั้นจึงA =:= Stringเป็นเช่นเดียวกับ=:=[A, String](และ=:=เป็นเพียงชั้นเรียนหรือลักษณะที่มีชื่อที่ดูดี) โปรดทราบว่าไวยากรณ์นี้ยังใช้งานได้กับคลาส "ปกติ" ตัวอย่างเช่นคุณสามารถเขียน:

val a: Tuple2[Int, String] = (1, "one")

แบบนี้:

val a: Int Tuple2 String = (1, "one")

มันคล้ายกับไวยากรณ์ทั้งสองสำหรับการเรียกใช้เมธอด "Normal" with .และ()ไวยากรณ์ของโอเปอเรเตอร์


2
ต้องการการโหวตเพราะmakes use of Scala's alternative infix syntax for type operators.คำอธิบายนี้ขาดหายไปโดยสิ้นเชิงซึ่งสิ่งทั้งหมดไม่สมเหตุสมผล
Ovidiu Dolha

39

อ่านคำตอบอื่น ๆ เพื่อทำความเข้าใจโครงสร้างเหล่านี้ นี่คือเวลาที่คุณควรใช้ คุณใช้มันเมื่อคุณต้องการ จำกัด วิธีการสำหรับประเภทเฉพาะเท่านั้น

นี่คือตัวอย่าง สมมติว่าคุณต้องการนิยามคู่ที่เป็นเนื้อเดียวกันดังนี้:

class Pair[T](val first: T, val second: T)

ตอนนี้คุณต้องการเพิ่มวิธีsmallerเช่นนี้:

def smaller = if (first < second) first else second

ใช้งานได้เฉพาะในกรณีที่Tมีการสั่งซื้อ คุณสามารถ จำกัด คลาสทั้งหมดได้:

class Pair[T <: Ordered[T]](val first: T, val second: T)

แต่นั่นเป็นความอัปยศ - อาจมีประโยชน์สำหรับชั้นเรียนเมื่อTไม่ได้รับคำสั่ง ด้วยข้อ จำกัด ประเภทคุณยังคงสามารถกำหนดsmallerวิธีการ:

def smaller(implicit ev: T <:< Ordered[T]) = if (first < second) first else second

มันก็โอเคที่จะยกตัวอย่างพูด, a Pair[File], ตราบใดที่คุณไม่ได้โทรหา smallerมัน

ในกรณีของOptionผู้ดำเนินการต้องการorNullวิธีแม้ว่ามันจะไม่สมเหตุสมผลOption[Int]ก็ตาม โดยใช้ข้อ จำกัด ประเภททั้งหมดเป็นอย่างดี คุณสามารถใช้orNullบนOption[String]และคุณสามารถสร้างOption[Int]และใช้งานได้ตราบใดที่คุณไม่โทรหาorNullมัน หากคุณลองSome(42).orNullคุณจะได้รับข้อความที่มีเสน่ห์

 error: Cannot prove that Null <:< Int

2
ฉันรู้ว่านี่เป็นเวลาหลายปีหลังจากคำตอบนี้ แต่ฉันกำลังมองหากรณีการใช้งาน<:<และฉันคิดว่าOrderedตัวอย่างไม่น่าสนใจอีกต่อไปแล้วตั้งแต่ตอนนี้คุณควรจะใช้Orderingtypeclass มากกว่าOrderedลักษณะนิสัย def smaller(implicit ord: Ordering[T]) = if (ord.lt(first, second)) first else secondสิ่งที่ชอบ:
ebruchez

1
@ebruchez: กรณีการใช้งานสำหรับการเข้ารหัสประเภทยูเนี่ยนในสกาล่าที่ไม่มีการแก้ไขโปรดดูmilessabin.com/blog/2011/06/09/scala-union-types-curry-howard

17

ขึ้นอยู่กับว่าพวกเขากำลังใช้อยู่ที่ไหน บ่อยที่สุดเมื่อใช้ในขณะที่ประกาศประเภทของพารามิเตอร์โดยนัยพวกเขาเป็นชั้นเรียน พวกเขาสามารถเป็นวัตถุในกรณีที่หายาก ในที่สุดพวกเขาสามารถเป็นผู้ดำเนินการเกี่ยวกับManifestวัตถุ พวกเขาถูกกำหนดไว้scala.Predefในสองกรณีแรกแม้ว่าจะไม่ได้รับการบันทึกไว้เป็นอย่างดี

พวกเขามีวัตถุประสงค์เพื่อให้วิธีการทดสอบความสัมพันธ์ระหว่างชั้นเรียนเช่นเดียวกับ<:และ<%ทำในสถานการณ์เมื่อหลังไม่สามารถใช้

สำหรับคำถามที่ว่า "ฉันควรใช้เมื่อไหร่" คำตอบคือคุณไม่ควรทำเว้นแต่คุณจะรู้ว่าคุณควรทำ :-) แก้ไข : ตกลงตกลงนี่คือตัวอย่างจากห้องสมุด เมื่อวันที่Eitherคุณมี:

/**
  * Joins an <code>Either</code> through <code>Right</code>.
  */
 def joinRight[A1 >: A, B1 >: B, C](implicit ev: B1 <:< Either[A1, C]): Either[A1, C] = this match {
   case Left(a)  => Left(a)
   case Right(b) => b
 }

 /**
  * Joins an <code>Either</code> through <code>Left</code>.
  */
 def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]): Either[C, B1] = this match {
   case Left(a)  => a
   case Right(b) => Right(b)
 }

เมื่อวันที่Optionคุณมี:

def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null

คุณจะพบตัวอย่างอื่น ๆ ในคอลเลกชัน


เป็น:-)อีกหนึ่งของเหล่านี้หรือไม่ และฉันก็เห็นด้วยว่าคำตอบของคุณคือ "ฉันควรใช้มันเมื่อไหร่" นำไปใช้กับหลายสิ่งที่ดี
Mike Miller

"พวกเขาตั้งใจจะให้วิธีทดสอบความสัมพันธ์ระหว่างชั้นเรียน" <- กว้างเกินไปที่จะเป็นประโยชน์
เจฟ

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