ฉันจะเชื่อมโยงนัยยะใน Scala ได้อย่างไร

รูปแบบ pimp-my-library ช่วยให้ฉันสามารถเพิ่มวิธีการให้กับชั้นเรียนได้โดยทำให้มีการแปลงโดยนัยจากคลาสนั้นไปเป็นคลาสที่ใช้เมธอดนั้น

สกาล่าไม่อนุญาตให้มีการแปลงโดยนัยสองครั้งดังกล่าว ดังนั้นฉันจึงไม่สามารถได้รับจาก A ถึง C โดยใช้โดยนัย A ถึง B และอีกอันโดยนัย B ถึง C มีวิธีแก้ไขข้อ จำกัด นี้หรือไม่?


person Daniel C. Sobral    schedule 16.03.2011    source แหล่งที่มา
comment
@ryeguy นี่คือคำถามเมตาสำหรับการถกเถียงแบบแมงดา/เพิ่มคุณค่า เพราะอึศักดิ์สิทธิ์ที่แท็กนั้น แท็กนั้น...   -  person Charles    schedule 15.06.2013


คำตอบ (3)


Scala มีข้อจำกัดในการแปลงอัตโนมัติในการเพิ่มวิธีการ กล่าวคือ จะไม่ใช้การแปลงมากกว่าหนึ่งรายการในการพยายามค้นหาวิธีการ ตัวอย่างเช่น:

class A(val n: Int)
class B(val m: Int, val n: Int)
class C(val m: Int, val n: Int, val o: Int) {
  def total = m + n + o
}

// This demonstrates implicit conversion chaining restrictions
object T1 { // to make it easy to test on REPL
  implicit def toA(n: Int): A = new A(n)
  implicit def aToB(a: A): B = new B(a.n, a.n)
  implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n)

  // won't work
  println(5.total)
  println(new A(5).total)

  // works
  println(new B(5, 5).total)
  println(new C(5, 5, 10).total)
}

แก้ไข: ขอบเขตการดู ('‹%') เลิกใช้แล้วตั้งแต่ Scala 2.11 https://issues.scala-lang.org/browse/SI-7629 (คุณสามารถใช้คลาสประเภทแทนได้)

อย่างไรก็ตาม หากคำจำกัดความโดยนัยต้องใช้พารามิเตอร์โดยนัยเอง (ขอบเขตของมุมมอง) Scala จะ ค้นหาค่าโดยนัยเพิ่มเติมตราบเท่าที่จำเป็น ต่อจากตัวอย่างสุดท้าย:

// def m[A <% B](m: A) is the same thing as
// def m[A](m: A)(implicit ev: A => B)

object T2 {
  implicit def toA(n: Int): A = new A(n)
  implicit def aToB[A1 <% A](a: A1): B = new B(a.n, a.n)
  implicit def bToC[B1 <% B](b: B1): C = new C(b.m, b.n, b.m + b.n)

  // works
  println(5.total)
  println(new A(5).total)
  println(new B(5, 5).total)
  println(new C(5, 5, 10).total)
}

"เวทมนตร์!" คุณอาจพูดว่า ไม่เป็นเช่นนั้น นี่คือวิธีที่คอมไพเลอร์จะแปลแต่ละรายการ:

object T1Translated {
  implicit def toA(n: Int): A = new A(n)
  implicit def aToB(a: A): B = new B(a.n, a.n)
  implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n)

  // Scala won't do this
  println(bToC(aToB(toA(5))).total)
  println(bToC(aToB(new A(5))).total)

  // Just this
  println(bToC(new B(5, 5)).total)

  // No implicits required
  println(new C(5, 5, 10).total)
}

object T2Translated {
  implicit def toA(n: Int): A = new A(n)
  implicit def aToB[A1 <% A](a: A1): B = new B(a.n, a.n)
  implicit def bToC[B1 <% B](b: B1): C = new C(b.m, b.n, b.m + b.n)

  // Scala does this
  println(bToC(5)(x => aToB(x)(y => toA(y))).total)
  println(bToC(new A(5))(x => aToB(x)(identity)).total)      
  println(bToC(new B(5, 5))(identity).total)

  // no implicits required
  println(new C(5, 5, 10).total)
}

ดังนั้น ในขณะที่ bToC ถูกใช้เป็นการแปลงโดยนัย aToB และ toA จะถูกส่งเป็นพารามิเตอร์โดยนัย แทนที่จะถูกโยงกันเป็นการแปลงโดยนัย

แก้ไข

คำถามที่เกี่ยวข้องที่น่าสนใจ:

person Daniel C. Sobral    schedule 16.03.2011
comment
คำอธิบายที่ดี เหตุผลที่ระบุไว้ในการไม่อนุญาตการเชื่อมโยงการแปลงโดยนัยคือเพื่อหลีกเลี่ยงความซับซ้อนและการแก้ไขจุดบกพร่องฝันร้าย ฉันสงสัยว่าเหตุใดจึงอนุญาตให้ผูกมัดกับพารามิเตอร์โดยนัยได้ - person Adrian; 17.03.2011
comment
ดี! ฉันได้เรียนรู้สิ่งใหม่ นี่ควรอยู่ในหน้าคุณสมบัติที่ซ่อนอยู่ - person Aaron Novstrup; 17.03.2011
comment
ขอบคุณ! ปัญหาเล็กน้อยประการหนึ่ง: ฉันต้องเพิ่มประเภทผลลัพธ์ที่ชัดเจนให้กับฟังก์ชัน aToB และ bToC ใน T2 เมื่อฉันลองใช้ใน REPL - person Agl; 04.04.2011
comment
@Agl มันจะไม่จำเป็นใน 2.9 ที่กำลังจะมาถึง แต่ฉันได้แก้ไขโค้ดเพื่อให้เข้ากันได้กับ 2.8 ขอบคุณ. - person Daniel C. Sobral; 04.04.2011
comment
โปรดทราบว่าการผูกมัดที่คุณพยายามทำนั้นเกี่ยวข้องกับประเภทที่มีเมตตาสูงกว่า จากนั้นการอนุมานประเภทก็สามารถช่วยคุณได้ เช่น. ฉันมีเอ็ม[เอ] ฉันมี A=›B และโดยนัย M[] =› N[] โดยที่ทั้ง M และ N เป็น monodic ฉันต้องการสร้าง N[B] โดยใช้การแปลงทั้งสอง การเชื่อมโยงสิ่งเหล่านี้จำเป็นต้องมีการเรียกเมธอดเพิ่มเติม วิธีแรกเพื่อจับ M[_] และวิธีที่สองเพื่อจับ A - person jsuereth; 11.04.2011
comment
@jsuereth ใช่นั่นอาจเป็นเรื่องยุ่งยาก ฉันจะดูว่าฉันสามารถหาตัวอย่างสั้นๆ ที่สมเหตุสมผลและเข้าใจได้หรือไม่ - person Daniel C. Sobral; 11.04.2011
comment
@Peter ดีกว่าที่จะถามคำถามเกี่ยวกับปัญหาเฉพาะนั้น ฉันคิดว่าการเพิ่มที่นี่จะทำให้ผิดหัวข้อ - person Daniel C. Sobral; 17.09.2011
comment
ขอบคุณ @DanielC.Sobral! คำตอบที่ยอดเยี่ยม ชี้แจงสิ่งต่าง ๆ ให้ฉันจริงๆ - person Tomer Gabel; 18.11.2013
comment
@ DanielC.Sobral นั่นเป็นคำตอบที่ยอดเยี่ยม! หลังจากอ่านไปสองสามครั้ง มันก็ดูไม่เหมือนวูดูบ้าๆ อีกต่อไป - person mo-seph; 18.12.2013
comment
ไม่มีขอบเขตมุมมอง: docs.scala-lang.org/tutorials/FAQ/ chaining-implicits.html (ดูความคิดเห็นด้านล่าง) - person eirirlar; 27.01.2016

โปรดทราบว่าคุณสามารถสร้างวงกลมด้วยพารามิเตอร์โดยนัยได้เช่นกัน อย่างไรก็ตาม สิ่งเหล่านี้ถูกตรวจพบโดยคอมไพเลอร์ ดังที่แสดงไว้ในสิ่งนี้:

class Wrap {
  class A(implicit b : B)
  class B(implicit c : C)
  class C(implicit a : A)

  implicit def c = new C
  implicit def b = new B
  implicit def a = new A
}

ข้อผิดพลาดที่มอบให้กับผู้ใช้นั้นไม่ชัดเจนเท่าที่ควร มันแค่บ่น could not find implicit value for parameter สำหรับสถานที่ก่อสร้างทั้งสามแห่ง นั่นอาจบดบังปัญหาที่ซ่อนอยู่ในกรณีที่ไม่ชัดเจน

person Raphael    schedule 17.03.2011

นี่คือโค้ดที่สะสมเส้นทางด้วย

import scala.language.implicitConversions

// Vertices
case class A(l: List[Char])
case class B(l: List[Char])
case class C(l: List[Char])
case class D(l: List[Char])
case class E(l: List[Char])

// Edges
implicit def ad[A1 <% A](x: A1) = D(x.l :+ 'A')
implicit def bc[B1 <% B](x: B1) = C(x.l :+ 'B')
implicit def ce[C1 <% C](x: C1) = E(x.l :+ 'C')
implicit def ea[E1 <% E](x: E1) = A(x.l :+ 'E')

def pathFrom(end:D) = end

pathFrom(B(Nil))   // res0: D = D(List(B, C, E, A))
person Sagie Davidovich    schedule 11.03.2013