ประเภทของการใช้ความหมาย
นัยใน Scala หมายถึงค่าที่สามารถส่งผ่าน "อัตโนมัติ" เพื่อพูดหรือแปลงจากประเภทหนึ่งไปเป็นอีกประเภทหนึ่งที่สร้างขึ้นโดยอัตโนมัติ
การแปลงโดยนัย
พูดสั้น ๆ มากเกี่ยวกับประเภทหลังถ้าใครเรียกวิธีการm
บนวัตถุo
ของชั้นC
และชั้นที่ไม่สนับสนุนวิธีการm
แล้ว Scala จะมองหาแปลงนัยจากC
สิ่งที่ไม่m
สนับสนุน ตัวอย่างง่ายๆจะเป็นวิธีการmap
ในString
:
"abc".map(_.toInt)
String
ไม่รองรับวิธีการmap
นี้ แต่StringOps
ทำได้และมีการแปลงโดยนัยจากString
เป็นStringOps
ใช้ได้ (ดูimplicit def augmentString
ที่Predef
)
พารามิเตอร์โดยปริยาย
ชนิดอื่น ๆ ของส่อเป็นนัยพารามิเตอร์ สิ่งเหล่านี้ถูกส่งผ่านไปยังการเรียกเมธอดเหมือนกับพารามิเตอร์อื่น ๆ แต่คอมไพเลอร์พยายามเติมให้เต็มโดยอัตโนมัติ หากไม่สามารถทำได้ก็จะบ่น หนึ่งสามารถส่งผ่านพารามิเตอร์เหล่านี้อย่างชัดเจนซึ่งเป็นวิธีการใช้breakOut
เช่น (ดูคำถามเกี่ยวกับbreakOut
ในวันที่คุณรู้สึกถึงความท้าทาย)
ในกรณีนี้เราต้องประกาศความต้องการโดยนัยเช่นfoo
การประกาศวิธีการ:
def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
ดูขอบเขต
มีสถานการณ์หนึ่งที่ implicit เป็นทั้งการแปลงโดยนัยและพารามิเตอร์ implicit ตัวอย่างเช่น:
def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)
getIndex("abc", 'a')
วิธีการที่getIndex
จะได้รับวัตถุใด ๆ Seq[T]
ตราบใดที่มีการแปลงนัยใช้ได้จากชั้นเลิศ ด้วยเหตุนี้ฉันสามารถส่งผ่านString
ไปยังgetIndex
และมันจะทำงาน
เบื้องหลังคอมไพเลอร์ที่มีการเปลี่ยนแปลงไปseq.IndexOf(value)
conv(seq).indexOf(value)
สิ่งนี้มีประโยชน์มากที่จะมีน้ำตาลซินแทคติคในการเขียน การใช้น้ำตาล syntactic นี้getIndex
สามารถกำหนดเช่นนี้:
def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)
น้ำตาลซินแทคติคนี้มีคำอธิบายว่าเป็นมุมมองที่ถูกผูกไว้คล้ายกับขอบบน ( CC <: Seq[Int]
) หรือขอบล่าง ( T >: Null
)
ขอบเขตของบริบท
อีกรูปแบบที่พบบ่อยในพารามิเตอร์นัยเป็นรูปแบบประเภทระดับ รูปแบบนี้เปิดใช้งานการจัดเตรียมอินเทอร์เฟซทั่วไปให้กับคลาสที่ไม่ได้ประกาศ ทั้งสองอย่างสามารถทำหน้าที่เป็นรูปแบบบริดจ์ได้โดยแยกข้อกังวลออกและเป็นรูปแบบของอะแดปเตอร์
Integral
ระดับที่คุณกล่าวถึงเป็นตัวอย่างที่คลาสสิกของรูปแบบคลาสประเภท Ordering
ตัวอย่างเช่นในห้องสมุดมาตรฐานสกาล่าก็คือ มีห้องสมุดที่ใช้รูปแบบนี้อย่างหนักเรียกว่า Scalaz
นี่คือตัวอย่างการใช้งาน:
def sum[T](list: List[T])(implicit integral: Integral[T]): T = {
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
นอกจากนี้ยังมีน้ำตาล syntactic สำหรับมันเรียกว่าบริบทที่ถูกผูกไว้ซึ่งทำให้มีประโยชน์น้อยลงโดยจำเป็นต้องอ้างถึงนัย การแปลงวิธีดังกล่าวจะมีลักษณะดังนี้:
def sum[T : Integral](list: List[T]): T = {
val integral = implicitly[Integral[T]]
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
ขอบเขตของบริบทมีประโยชน์มากขึ้นเมื่อคุณต้องการส่งผ่านไปยังวิธีอื่นที่ใช้งาน ตัวอย่างเช่นวิธีการsorted
เกี่ยวกับความต้องการโดยปริยายSeq
Ordering
ในการสร้างวิธีการreverseSort
หนึ่งสามารถเขียน:
def reverseSort[T : Ordering](seq: Seq[T]) = seq.sorted.reverse
เพราะOrdering[T]
ถูกส่งผ่านไปโดยปริยายreverseSort
จึงสามารถส่งต่อไปโดยปริยายsorted
ได้
Implicits มาจากไหน
เมื่อคอมไพเลอร์เห็นความต้องการปริยายเนื่องจากคุณกำลังเรียกใช้เมธอดที่ไม่มีอยู่ในคลาสของวัตถุหรือเนื่องจากคุณกำลังเรียกใช้เมธอดที่ต้องการพารามิเตอร์ implicit มันจะค้นหา implicit ที่เหมาะสมกับความต้องการ .
การค้นหานี้ปฏิบัติตามกฎบางอย่างที่กำหนดว่า implicits ใดที่สามารถมองเห็นได้ ตารางต่อไปนี้แสดงให้เห็นว่าผู้รวบรวมจะค้นหา implicits จากการนำเสนอที่ยอดเยี่ยมเกี่ยวกับการ implicits โดย Josh Suereth ซึ่งฉันแนะนำให้ทุกคนที่ต้องการพัฒนาความรู้สกาล่าของพวกเขาอย่างเต็มที่ มันได้รับการเติมเต็มตั้งแต่นั้นมาพร้อมกับข้อเสนอแนะและการปรับปรุง
implicits ที่มีอยู่ภายใต้หมายเลข 1 ด้านล่างมีความสำคัญเหนือกว่าภายใต้หมายเลข 2 นอกเหนือจากนั้นหากมีข้อโต้แย้งที่มีสิทธิ์หลายอย่างซึ่งตรงกับประเภทของพารามิเตอร์ implicit หนึ่งที่เฉพาะเจาะจงที่สุดจะถูกเลือกโดยใช้กฎของการแก้ไขปัญหา ข้อมูลจำเพาะ§6.26.3) ข้อมูลรายละเอียดเพิ่มเติมสามารถพบได้ในคำถามที่ฉันลิงก์ไปที่ท้ายคำตอบนี้
- ดูครั้งแรกในขอบเขตปัจจุบัน
- นัยที่กำหนดไว้ในขอบเขตปัจจุบัน
- การนำเข้าที่ชัดเจน
- สัญลักษณ์ตัวแทนนำเข้า
ขอบเขตเดียวกันในไฟล์อื่น ๆ
- ตอนนี้ดูประเภทที่เกี่ยวข้องใน
- วัตถุร่วมประเภท
- ขอบเขตของประเภทอาร์กิวเมนต์โดยนัย(2.9.1)
- ขอบเขตโดยนัยของอาร์กิวเมนต์ชนิด(2.8.0)
- วัตถุภายนอกสำหรับชนิดซ้อนกัน
- มิติอื่น ๆ
ลองยกตัวอย่างสำหรับพวกเขา:
นัยที่กำหนดไว้ในขอบเขตปัจจุบัน
implicit val n: Int = 5
def add(x: Int)(implicit y: Int) = x + y
add(5) // takes n from the current scope
การนำเข้าที่ชัดเจน
import scala.collection.JavaConversions.mapAsScalaMap
def env = System.getenv() // Java map
val term = env("TERM") // implicit conversion from Java Map to Scala Map
สัญลักษณ์ตัวแทนนำเข้า
def sum[T : Integral](list: List[T]): T = {
val integral = implicitly[Integral[T]]
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
ขอบเขตเดียวกันในไฟล์อื่น ๆ
แก้ไข : ดูเหมือนว่านี่จะไม่มีลำดับความสำคัญแตกต่างกัน หากคุณมีตัวอย่างที่แสดงให้เห็นถึงความแตกต่างที่มีความสำคัญโปรดแสดงความคิดเห็น มิฉะนั้นอย่าพึ่งพาสิ่งนี้
นี่เป็นเหมือนตัวอย่างแรก แต่สมมติว่าความหมายโดยนัยอยู่ในไฟล์ที่แตกต่างจากการใช้งาน ดูเพิ่มเติมว่าอาจใช้วัตถุแพ็กเกจในการนำ implicits อย่างไร
วัตถุร่วมของประเภท
มีโน้ตของวัตถุสองรายการที่นี่ ขั้นแรกให้มองหาคู่หูของวัตถุที่เป็น "แหล่งที่มา" ยกตัวอย่างเช่นภายในวัตถุOption
มีการแปลงส่อไปIterable
ดังนั้นหนึ่งสามารถเรียกIterable
วิธีการในOption
หรือผ่านไปยังสิ่งที่คาดหวังว่าOption
Iterable
ตัวอย่างเช่น:
for {
x <- List(1, 2, 3)
y <- Some('x')
} yield (x, y)
นิพจน์นั้นแปลโดยคอมไพเลอร์เป็น
List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))
อย่างไรก็ตามList.flatMap
คาดว่า a TraversableOnce
ซึ่งOption
ไม่ใช่ คอมไพเลอร์นั้นจะมองเข้าไปข้างในOption
วัตถุที่เป็นเพื่อนร่วมทางและพบว่าการแปลงเป็นIterable
ซึ่งTraversableOnce
ทำให้การแสดงออกนี้ถูกต้อง
ประการที่สองวัตถุร่วมของประเภทที่คาดหวัง:
List(1, 2, 3).sorted
วิธีการที่จะใช้เวลาโดยปริยายsorted
Ordering
ในกรณีนี้จะมีลักษณะภายในวัตถุOrdering
สหายในชั้นเรียนOrdering
และพบนัยOrdering[Int]
มี
โปรดสังเกตว่าวัตถุที่เป็นคู่หูของคลาสพิเศษนั้นจะถูกมองด้วย ตัวอย่างเช่น:
class A(val n: Int)
object A {
implicit def str(a: A) = "A: %d" format a.n
}
class B(val x: Int, y: Int) extends A(y)
val b = new B(5, 2)
val s: String = b // s == "A: 2"
นี่คือวิธีที่สกาล่าพบโดยปริยายNumeric[Int]
และNumeric[Long]
ในคำถามของคุณโดยวิธีการที่พวกเขาจะพบว่าข้างในไม่ได้Numeric
Integral
ขอบเขตของประเภทอาร์กิวเมนต์โดยปริยาย
หากคุณมีเมธอดที่มีชนิดอาร์กิวเมนต์A
ขอบเขตของชนิดA
นั้นจะถูกพิจารณาด้วย โดย "ขอบเขตโดยนัย" ฉันหมายความว่ากฎเหล่านี้ทั้งหมดจะถูกนำไปใช้ซ้ำ - ตัวอย่างเช่นวัตถุที่เป็นสหายของA
จะถูกค้นหาโดยนัยตามกฎข้างต้น
โปรดทราบว่าสิ่งนี้ไม่ได้หมายความว่าขอบเขตโดยนัยของA
จะถูกค้นหาการแปลงของพารามิเตอร์นั้น แต่เป็นการแสดงออกทั้งหมด ตัวอย่างเช่น:
class A(val n: Int) {
def +(other: A) = new A(n + other.n)
}
object A {
implicit def fromInt(n: Int) = new A(n)
}
// This becomes possible:
1 + new A(1)
// because it is converted into this:
A.fromInt(1) + new A(1)
มีให้ตั้งแต่ Scala 2.9.1
ขอบเขตของอาร์กิวเมนต์ชนิดโดยนัย
สิ่งนี้จะต้องทำให้รูปแบบคลาสของคลาสใช้งานได้จริง ลองพิจารณาOrdering
ตัวอย่าง: มันมาพร้อมกับนัยในวัตถุที่เป็นคู่หู แต่คุณไม่สามารถเพิ่มสิ่งต่าง ๆ ลงไปได้ ดังนั้นคุณจะสร้างOrdering
ชั้นเรียนของคุณเองโดยอัตโนมัติได้อย่างไร?
เริ่มจากการใช้งาน:
class A(val n: Int)
object A {
implicit val ord = new Ordering[A] {
def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n)
}
}
ดังนั้นให้พิจารณาสิ่งที่เกิดขึ้นเมื่อคุณโทร
List(new A(5), new A(2)).sorted
อย่างที่เราเห็นวิธีการsorted
คาดหวังOrdering[A]
(จริง ๆ แล้วมันคาดหวังว่าOrdering[B]
ที่ไหนB >: A
) ไม่มีสิ่งใดอยู่ข้างในOrdering
และไม่มีประเภท "แหล่งที่มา" ที่จะดู เห็นได้ชัดว่ามันคือการหามันไว้ภายในA
ซึ่งเป็นอาร์กิวเมนต์ชนิดOrdering
ของ
และนี่ก็เป็นวิธีการวิธีการเก็บรวบรวมคาดหวังต่างๆCanBuildFrom
ทำงาน: implicits CanBuildFrom
ที่พบภายในวัตถุสหายกับพารามิเตอร์ประเภทของ
หมายเหตุ : Ordering
ถูกกำหนดเป็นtrait Ordering[T]
โดยที่T
พารามิเตอร์ประเภท ก่อนหน้านี้ฉันบอกว่า Scala ดูพารามิเตอร์ประเภทภายในซึ่งไม่สมเหตุสมผลนัก นัยมองหาข้างต้นเป็นOrdering[A]
ที่A
เป็นประเภทที่เกิดขึ้นจริงไม่ได้พิมพ์พารามิเตอร์: มันเป็นอาร์กิวเมนต์ชนิดOrdering
ไป ดูหัวข้อ 7.2 ของข้อมูลจำเพาะสกาล่า
มีให้ตั้งแต่ Scala 2.8.0
วัตถุภายนอกสำหรับประเภทซ้อนกัน
ฉันไม่ได้เห็นตัวอย่างของสิ่งนี้จริงๆ ฉันจะขอบคุณถ้ามีใครสามารถแบ่งปัน หลักการง่าย ๆ :
class A(val n: Int) {
class B(val m: Int) { require(m < n) }
}
object A {
implicit def bToString(b: A#B) = "B: %d" format b.m
}
val a = new A(5)
val b = new a.B(3)
val s: String = b // s == "B: 3"
มิติอื่น ๆ
ฉันค่อนข้างแน่ใจว่านี่เป็นเรื่องตลก แต่คำตอบนี้อาจไม่ทันสมัย ดังนั้นอย่าใช้คำถามนี้ว่าเป็นผู้ตัดสินคนสุดท้ายของสิ่งที่เกิดขึ้นและหากคุณสังเกตว่ามันล้าสมัยแล้วโปรดแจ้งให้ฉันทราบเพื่อที่ฉันจะได้แก้ไขได้
แก้ไข
คำถามที่น่าสนใจที่เกี่ยวข้อง: