ในวิธีง่าย ๆ บริบทและมุมมองขอบเขตคืออะไรและอะไรคือความแตกต่างระหว่างพวกเขา
ตัวอย่างที่ง่ายต่อการติดตามจะดีเช่นกัน!
ในวิธีง่าย ๆ บริบทและมุมมองขอบเขตคืออะไรและอะไรคือความแตกต่างระหว่างพวกเขา
ตัวอย่างที่ง่ายต่อการติดตามจะดีเช่นกัน!
คำตอบ:
ฉันคิดว่าคำถามนี้ถูกถามไปแล้ว แต่ถ้าเป็นเช่นนั้นคำถามจะไม่ปรากฏในแถบ "ที่เกี่ยวข้อง" ดังนั้นนี่คือ:
มุมมองที่ถูกผูกไว้เป็นกลไกที่นำมาใช้ในการเปิดใช้งานกาลาใช้ชนิดบาง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): Booleana < 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 เพื่อทำความคุ้นเคยกับวิธีการทั้งหมดที่สามารถใช้ได้
แก้ไข
คำถามที่เกี่ยวข้องที่น่าสนใจ: