“ บริบทผูกพัน” ใน Scala คืออะไร?


115

หนึ่งในคุณสมบัติใหม่ของ Scala 2.8 คือขอบเขตบริบท บริบทที่ผูกไว้คืออะไรและมีประโยชน์ที่ใด

แน่นอนฉันค้นหาก่อน (และพบเช่นนี้ ) แต่ฉันไม่พบข้อมูลที่ชัดเจนและละเอียดมาก


8
ตรวจสอบสิ่งนี้สำหรับทัวร์ทุกประเภท: gist.github.com/257758/47f06f2f3ca47702b3a86c76a5479d096cb8c7ec
Arjan Blokzijl

2
คำตอบที่ยอดเยี่ยมนี้เปรียบเทียบ / ตัดกันขอบเขตบริบทและขอบเขตมุมมอง: stackoverflow.com/questions/4465948/…
Aaron Novstrup

นี่เป็นคำตอบที่ดีมากstackoverflow.com/a/25250693/1586965
samthebest

คำตอบ:


107

คุณพบบทความนี้หรือไม่? ครอบคลุมคุณลักษณะบริบทใหม่ที่ถูกผูกไว้ภายในบริบทของการปรับปรุงอาร์เรย์

โดยทั่วไปพารามิเตอร์ชนิดที่มีบริบทที่ถูกผูกไว้เป็นของแบบฟอร์ม[T: Bound]; มันจะขยายไปยังประเภทธรรมดาพารามิเตอร์ร่วมกับพารามิเตอร์โดยนัยของประเภท TBound[T]

พิจารณาวิธีการtabulateที่สร้างอาร์เรย์จากผลลัพธ์ของการใช้ฟังก์ชันที่กำหนด f ในช่วงของตัวเลขตั้งแต่ 0 จนถึงความยาวที่กำหนด มากถึง Scala 2.7 ตารางสามารถเขียนได้ดังนี้:

def tabulate[T](len: Int, f: Int => T) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}

ใน Scala 2.8 เป็นไปไม่ได้อีกต่อไปเนื่องจากข้อมูลรันไทม์เป็นสิ่งที่จำเป็นในการสร้างการแสดงที่ถูกต้องของArray[T]. เราจำเป็นต้องให้ข้อมูลนี้โดยส่งผ่าน a ClassManifest[T]เข้าไปใน method เป็นพารามิเตอร์โดยปริยาย:

def tabulate[T](len: Int, f: Int => T)(implicit m: ClassManifest[T]) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}

ในรูปแบบชวเลขสามารถใช้บริบทขอบเขตกับพารามิเตอร์ type Tแทนโดยให้:

def tabulate[T: ClassManifest](len: Int, f: Int => T) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}

145

คำตอบของโรเบิร์ตครอบคลุมรายละเอียดทางเทคนิคของขอบเขตบริบท ฉันจะให้คุณตีความความหมายของมัน

ใน Scala a View Bound ( A <% B) รวบรวมแนวคิดของ 'can be seen as' (ในขณะที่ขอบเขตด้านบน<:รวบรวมแนวคิดของ 'is a') บริบทที่ถูกผูกไว้ ( A : C) ระบุว่า 'มี' เกี่ยวกับประเภท คุณสามารถอ่านตัวอย่างเกี่ยวกับรายการที่เป็น " TมีManifest" ตัวอย่างที่คุณเชื่อมโยงเกี่ยวกับOrderedvs Orderingแสดงให้เห็นถึงความแตกต่าง วิธีการ

def example[T <% Ordered[T]](param: T)

บอกว่าพารามิเตอร์สามารถมองเห็นเป็นOrderedไฟล์. เปรียบเทียบกับ

def example[T : Ordering](param: T)

ซึ่งบอกว่าพารามิเตอร์มีการเชื่อมโยง Orderingที่บอกว่าพารามิเตอร์มีที่เกี่ยวข้อง

ในแง่ของการใช้งานต้องใช้เวลาสักพักในการสร้างอนุสัญญา แต่ควรใช้ขอบเขตบริบทมากกว่าขอบเขตการดู ( ตอนนี้เลิกใช้งานขอบเขตการดูแล้ว ) ข้อเสนอแนะอย่างหนึ่งคือควรใช้บริบทที่ถูกผูกไว้เมื่อคุณต้องการถ่ายโอนคำจำกัดความโดยนัยจากขอบเขตหนึ่งไปยังอีกขอบเขตหนึ่งโดยไม่จำเป็นต้องอ้างถึงโดยตรง (แน่นอนว่าเป็นกรณีที่ ClassManifestใช้ในการสร้างอาร์เรย์)

อีกวิธีหนึ่งในการคิดเกี่ยวกับขอบเขตการดูและขอบเขตบริบทคือการถ่ายโอนการแปลงโดยนัยจากขอบเขตของผู้โทรครั้งแรก สิ่งที่สองถ่ายโอนวัตถุโดยนัยจากขอบเขตของผู้โทร


2
"มี" มากกว่า "เป็น" หรือ "เห็นว่า" เป็นข้อมูลเชิงลึกที่สำคัญสำหรับฉัน - ไม่เห็นสิ่งนี้ในคำอธิบายอื่นใด การมีตัวดำเนินการ / ฟังก์ชั่นที่คลุมเครือเล็กน้อยในเวอร์ชันภาษาอังกฤษทำให้การดูดซับง่ายขึ้นมาก - ขอบคุณ!
DNA

1
@ Ben Lings คุณหมายถึงอะไร .... 'มี' เกี่ยวกับประเภท ... ? อะไรคือสิ่งที่เกี่ยวกับประเภท ?
jhegedus

1
@jhegedus นี่คือการแยกวิเคราะห์ของฉัน: "เกี่ยวกับประเภท" หมายความว่า A หมายถึงประเภท วลี "has a" มักใช้ในการออกแบบเชิงวัตถุเพื่ออธิบายความสัมพันธ์ของวัตถุ (เช่นลูกค้า "มี" ที่อยู่) แต่ที่นี่ความสัมพันธ์ "มี" คือระหว่างประเภทไม่ใช่วัตถุ เป็นการเปรียบเทียบแบบหลวม ๆ เพราะความสัมพันธ์แบบ "มี" นั้นไม่ได้มีมา แต่กำเนิดหรือเป็นสากลเหมือนในการออกแบบ OO ลูกค้ามีที่อยู่เสมอ แต่สำหรับบริบทที่ผูก A ไม่ได้มี C เสมอไป แต่บริบทที่ผูกไว้จะระบุว่าต้องระบุอินสแตนซ์ของ C [A] โดยปริยาย
jbyler

ฉันเรียนรู้ Scala มาหนึ่งเดือนแล้วและนี่คือคำอธิบายที่ดีที่สุดที่ฉันได้เห็นในเดือนนี้! ขอบคุณ @ เบ็น!
Lifu Huang

@ Ben Lings: ขอบคุณหลังจากใช้เวลานานมากในการทำความเข้าใจกับบริบทที่ผูกพันคำตอบของคุณมีประโยชน์มาก [ has aเหมาะสมกับฉันมากขึ้น]
Shankar

39

(นี่คือบันทึกย่อโปรดอ่านและทำความเข้าใจคำตอบอื่น ๆ ก่อน)

ขอบเขตบริบททำให้เกิดขอบเขตการดูโดยทั่วไป

ดังนั้นให้รหัสนี้แสดงด้วย View Bound:

scala> implicit def int2str(i: Int): String = i.toString
int2str: (i: Int)String

scala> def f1[T <% String](t: T) = 0
f1: [T](t: T)(implicit evidence$1: (T) => String)Int

นอกจากนี้ยังอาจจะแสดงออกกับบริบทที่ถูกผูกไว้ด้วยความช่วยเหลือของนามแฝงประเภทที่เป็นตัวแทนของฟังก์ชั่นจากประเภทประเภทFT

scala> trait To[T] { type From[F] = F => T }           
defined trait To

scala> def f2[T : To[String]#From](t: T) = 0       
f2: [T](t: T)(implicit evidence$1: (T) => java.lang.String)Int

scala> f2(1)
res1: Int = 0

* => *บริบทผูกพันต้องใช้กับตัวสร้างประเภทของชนิด อย่างไรก็ตามประเภทสร้างเป็นของชนิดFunction1 (*, *) => *การใช้นามแฝงประเภทบางส่วนใช้พารามิเตอร์ประเภทที่สองกับประเภทStringโดยให้ตัวสร้างชนิดที่ถูกต้องเพื่อใช้เป็นขอบเขตบริบท

มีข้อเสนอเพื่อให้คุณสามารถแสดงประเภทที่ใช้บางส่วนใน Scala ได้โดยตรงโดยไม่ต้องใช้นามแฝงประเภทภายในลักษณะ จากนั้นคุณสามารถเขียน:

def f3[T : [X](X => String)](t: T) = 0 

คุณช่วยอธิบายความหมายของ #From ในนิยามของ f2 ได้ไหม ฉันไม่แน่ใจว่าจะสร้างแบบ F ที่ไหน (ฉันพูดถูกหรือเปล่า)
Collin

1
มันเรียกว่าการฉายประเภทอ้างอิงเป็นสมาชิกประเภทประเภทFrom To[String]เราไม่ได้ให้อาร์กิวเมนต์ประเภทFromดังนั้นเราจึงอ้างถึงตัวสร้างประเภทไม่ใช่ประเภท คอนสตรัคชนิดนี้เป็นชนิดที่เหมาะสมที่จะใช้เป็นบริบทผูกพัน * -> *- นี้ขอบเขตพารามิเตอร์ชนิดโดยกำหนดพารามิเตอร์โดยนัยของประเภทT To[String]#From[T]ขยายประเภทนามแฝงและ voila ที่คุณเหลือFunction1[String, T]อยู่
retronym

นั่นควรเป็น Function1 [T, String] หรือไม่?
ssanj

18

นี่เป็นบันทึกย่ออื่น

ดังที่Ben ชี้ให้เห็นบริบทที่ถูกผูกไว้แสดงถึงข้อ จำกัด "has-a" ระหว่างพารามิเตอร์ type และคลาส type กล่าวอีกนัยหนึ่งคือแสดงถึงข้อ จำกัด ที่มีค่าโดยนัยของคลาสชนิดเฉพาะ

เมื่อใช้บริบทที่ถูกผูกไว้มักจะต้องแสดงให้เห็นถึงคุณค่าโดยนัยนั้น ตัวอย่างเช่นเมื่อพิจารณาถึงข้อ จำกัดT : Orderingมักจะต้องมีตัวอย่างOrdering[T]ที่ตรงตามข้อ จำกัด ดังที่แสดงไว้ที่นี่คุณสามารถเข้าถึงค่าโดยนัยได้โดยใช้implicitlyวิธีการหรือวิธีที่เป็นประโยชน์มากกว่าเล็กน้อยcontext:

def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) = 
   xs zip ys map { t => implicitly[Numeric[T]].times(t._1, t._2) }

หรือ

def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) =
   xs zip ys map { t => context[T]().times(t._1, t._2) }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.