ในวิธีง่าย ๆ บริบทและมุมมองขอบเขตคืออะไรและอะไรคือความแตกต่างระหว่างพวกเขา
ตัวอย่างที่ง่ายต่อการติดตามจะดีเช่นกัน!
ในวิธีง่าย ๆ บริบทและมุมมองขอบเขตคืออะไรและอะไรคือความแตกต่างระหว่างพวกเขา
ตัวอย่างที่ง่ายต่อการติดตามจะดีเช่นกัน!
คำตอบ:
ฉันคิดว่าคำถามนี้ถูกถามไปแล้ว แต่ถ้าเป็นเช่นนั้นคำถามจะไม่ปรากฏในแถบ "ที่เกี่ยวข้อง" ดังนั้นนี่คือ:
มุมมองที่ถูกผูกไว้เป็นกลไกที่นำมาใช้ในการเปิดใช้งานกาลาใช้ชนิดบางA
ราวกับว่าB
มันเป็นบางชนิด ไวยากรณ์ทั่วไปคือ:
def f[A <% B](a: A) = a.bMethod
ในคำอื่น ๆA
ควรจะมีการแปลงโดยปริยายที่จะB
สามารถใช้งานได้เพื่อให้เราสามารถเรียกวิธีการเกี่ยวกับวัตถุของการพิมพ์B
A
การใช้ขอบเขตการดูโดยทั่วไปในไลบรารีมาตรฐาน (ก่อนหน้าสกาล่า 2.8.0 อยู่ดี) มีOrdered
ดังนี้:
def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b
เพราะสามารถแปลงA
เป็นOrdered[A]
และเพราะOrdered[A]
กำหนดวิธีการที่จะสามารถใช้การแสดงออก<(other: A): Boolean
a < b
โปรดทราบว่าขอบเขตการดูไม่ได้รับการสนับสนุนคุณควรหลีกเลี่ยง
ขอบเขตบริบทได้รับการแนะนำใน Scala 2.8.0 และโดยทั่วไปจะใช้กับรูปแบบคลาสของชนิดที่เรียกว่าเป็นรูปแบบของโค้ดที่เลียนแบบฟังก์ชันการทำงานที่จัดทำโดยคลาสประเภท Haskell แม้ว่าในลักษณะที่ละเอียดมากขึ้น
ในขณะที่มุมมองที่ถูกผูกไว้สามารถใช้กับประเภทที่เรียบง่าย (ตัวอย่างเช่นA <% String
) บริบทที่ถูกผูกไว้ต้องใช้ประเภทพารามิเตอร์เช่นOrdered[A]
ด้านบน แต่ไม่เหมือนString
กัน
บริบทผูกพันอธิบายนัยค่าแทนของมุมมองผูกพันของนัยแปลง มันถูกใช้เพื่อประกาศว่าสำหรับบางประเภทA
มีค่าโดยนัยของประเภทที่B[A]
มีอยู่ ไวยากรณ์เป็นดังนี้:
def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]
นี่คือความสับสนมากกว่ามุมมองที่ถูกผูกไว้เพราะมันไม่ชัดเจนว่าจะใช้งานได้อย่างไร ตัวอย่างทั่วไปของการใช้งานใน Scala คือ:
def f[A : ClassManifest](n: Int) = new Array[A](n)
การกำหนดArray
ค่าเริ่มต้นสำหรับชนิดที่กำหนดพารามิเตอร์ต้องมีClassManifest
ให้พร้อมใช้งานสำหรับเหตุผลที่เกี่ยวข้องกับการลบประเภทและลักษณะที่ไม่ถูกลบของอาร์เรย์
อีกตัวอย่างที่พบบ่อยมากในไลบรารีนั้นซับซ้อนกว่าเล็กน้อย:
def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)
ที่นี่implicitly
จะใช้ในการดึงค่าปริยายเราต้องการเป็นหนึ่งในประเภทซึ่งระดับกำหนดวิธีการOrdering[A]
compare(a: A, b: A): Int
เราจะเห็นอีกวิธีหนึ่งในการทำสิ่งนี้ด้านล่าง
ไม่น่าแปลกใจเลยที่ทั้งขอบเขตการดูและขอบเขตบริบทจะถูกนำไปใช้กับพารามิเตอร์โดยปริยาย ที่จริงแล้วไวยากรณ์ที่ฉันแสดงให้เห็นคือน้ำตาลประโยคสำหรับสิ่งที่เกิดขึ้นจริง ดูวิธีลดน้ำตาลได้ที่ด้านล่าง:
def f[A <% B](a: A) = a.bMethod
def f[A](a: A)(implicit ev: A => B) = a.bMethod
def g[A : B](a: A) = h(a)
def g[A](a: A)(implicit ev: B[A]) = h(a)
ดังนั้นโดยธรรมชาติเราสามารถเขียนพวกเขาในรูปแบบเต็มซึ่งเป็นประโยชน์สำหรับขอบเขตบริบท:
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)
ขอบเขตการดูส่วนใหญ่จะใช้เพื่อใช้ประโยชน์จากรูปแบบไลบรารีของฉันซึ่งวิธีการ "เพิ่ม" ไปยังคลาสที่มีอยู่ในสถานการณ์ที่คุณต้องการส่งคืนชนิดดั้งเดิม หากคุณไม่ต้องการคืนค่าประเภทนั้นในทางใดทางหนึ่งคุณไม่จำเป็นต้องมีมุมมองที่ถูกผูกไว้
Ordered
ตัวอย่างคลาสสิกในมุมมองของการใช้งานที่ถูกผูกไว้คือการจัดการ โปรดทราบว่าInt
ไม่ใช่Ordered
ตัวอย่างเช่นแม้ว่าจะมีการแปลงโดยนัย ตัวอย่างที่ให้ไว้ก่อนหน้านี้ต้องการมุมมองที่ถูกผูกไว้เพราะมันคืนค่าชนิดที่ไม่ถูกแปลง
def f[A <% Ordered[A]](a: A, b: A): A = if (a < b) a else b
ตัวอย่างนี้จะไม่ทำงานหากไม่มีขอบเขตการดู อย่างไรก็ตามหากฉันต้องส่งคืนประเภทอื่นฉันไม่ต้องการมุมมองที่ถูกผูกไว้อีกต่อไป:
def f[A](a: Ordered[A], b: A): Boolean = a < b
การแปลงที่นี่ (ถ้าจำเป็น) เกิดขึ้นก่อนที่ฉันจะส่งพารามิเตอร์ไปf
ให้ดังนั้นf
ไม่จำเป็นต้องรู้
นอกจากนี้Ordered
การใช้งานที่พบบ่อยที่สุดจากไลบรารีคือการจัดการString
และArray
ซึ่งเป็นคลาส Java เช่นเดียวกับคอลเลกชัน Scala ตัวอย่างเช่น:
def f[CC <% Traversable[_]](a: CC, b: CC): CC = if (a.size < b.size) a else b
หากหนึ่งในความพยายามที่จะทำเช่นนี้โดยมุมมองขอบเขตประเภทการกลับมาของString
จะเป็นWrappedString
(Scala 2.8) Array
และในทำนองเดียวกันสำหรับ
สิ่งเดียวกันจะเกิดขึ้นแม้ว่าประเภทจะใช้เป็นพารามิเตอร์ประเภทของประเภทส่งคืนเท่านั้น:
def f[A <% Ordered[A]](xs: A*): Seq[A] = xs.toSeq.sorted
ขอบเขตบริบทส่วนใหญ่จะใช้ในสิ่งที่เป็นที่รู้จักในฐานะรูปแบบ typeclassเป็นการอ้างอิงถึงคลาสประเภทของ Haskell โดยทั่วไปแล้วรูปแบบนี้ใช้ทางเลือกในการสืบทอดโดยทำให้สามารถใช้งานได้ผ่านทางรูปแบบของอะแดปเตอร์โดยนัย
ตัวอย่างคลาสสิกคือ Scala 2.8's Ordering
ซึ่งแทนที่Ordered
ตลอดทั้งห้องสมุดของ Scala การใช้งานคือ:
def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b
แม้ว่าโดยปกติคุณจะเห็นว่าเขียนแบบนี้:
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = {
import ord.mkOrderingOps
if (a < b) a else b
}
ซึ่งใช้ประโยชน์จากการแปลงโดยนัยบางอย่างภายในOrdering
ที่เปิดใช้งานรูปแบบโอเปอเรเตอร์แบบดั้งเดิม อีกตัวอย่างหนึ่งใน Scala 2.8 คือNumeric
:
def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b)
ตัวอย่างที่ซับซ้อนมากขึ้นคือการใช้คอลเล็กชันใหม่CanBuildFrom
แต่มีคำตอบที่ยาวมากเกี่ยวกับเรื่องนั้นดังนั้นฉันจะหลีกเลี่ยงได้ที่นี่ และตามที่กล่าวไว้ก่อนหน้านี้มีการClassManifest
ใช้งานซึ่งจำเป็นต้องมีเพื่อเริ่มต้นอาร์เรย์ใหม่โดยไม่มีประเภทที่เป็นรูปธรรม
บริบทที่ผูกกับรูปแบบของ typeclass นั้นมีแนวโน้มที่จะถูกใช้โดยคลาสของคุณเองเนื่องจากมันช่วยให้แยกความกังวลออกไปได้ในขณะที่ขอบเขตการดูสามารถหลีกเลี่ยงได้ในโค้ดของคุณเองด้วยการออกแบบที่ดี )
แม้ว่ามันจะเป็นไปได้เป็นเวลานาน แต่การใช้ขอบเขตบริบทได้ถูกนำออกไปจริง ๆ ในปี 2010 และขณะนี้พบได้ในระดับหนึ่งในห้องสมุดและกรอบงานที่สำคัญที่สุดของสกาล่า อย่างไรก็ตามตัวอย่างที่สุดยอดของการใช้งานก็คือห้องสมุด Scalaz ซึ่งนำพลังของ Haskell มาสู่ Scala ฉันแนะนำให้อ่านข้อมูลเกี่ยวกับรูปแบบ typeclass เพื่อทำความคุ้นเคยกับวิธีการทั้งหมดที่สามารถใช้ได้
แก้ไข
คำถามที่เกี่ยวข้องที่น่าสนใจ: