การตอบคำถามหนึ่งถึงสาม: แอปพลิเคชันหลักอย่างหนึ่งสำหรับการHLists
สรุปที่เป็นนามธรรม โดยทั่วไปแล้ว Arity นั้นจะรู้จักกันในไซต์การใช้งานที่เป็นนามธรรม แต่จะแตกต่างกันไปตามแต่ละไซต์ เวลานี้จากไม่มีรูปแบบของตัวอย่าง ,
def flatten[T <: Product, L <: HList](t : T)
(implicit hl : HListerAux[T, L], flatten : Flatten[L]) : flatten.Out =
flatten(hl(t))
val t1 = (1, ((2, 3), 4))
val f1 = flatten(t1) // Inferred type is Int :: Int :: Int :: Int :: HNil
val l1 = f1.toList // Inferred type is List[Int]
val t2 = (23, ((true, 2.0, "foo"), "bar"), (13, false))
val f2 = flatten(t2)
val t2b = f2.tupled
// Inferred type of t2b is (Int, Boolean, Double, String, String, Int, Boolean)
โดยไม่ต้องใช้HLists
(หรือสิ่งที่เทียบเท่า) เพื่อนามธรรมเหนือ arity ของอาร์กิวเมนต์ tuple flatten
มันจะเป็นไปไม่ได้ที่จะมีการใช้งานเพียงครั้งเดียวซึ่งสามารถยอมรับข้อโต้แย้งของทั้งสองรูปร่างที่แตกต่างกันมากและเปลี่ยนพวกเขาในวิธีที่ปลอดภัยประเภท
ความสามารถในการนามธรรมเหนือ arity น่าจะเป็นที่สนใจทุกที่ที่ arities คงที่เกี่ยวข้อง: เช่นเดียวกับ tuples ดังกล่าวข้างต้นที่มีรายการพารามิเตอร์วิธี / ฟังก์ชั่นและชั้นเรียนกรณี ดูที่นี่สำหรับตัวอย่างของวิธีที่เราอาจสรุปความ arity ของคลาสเคสที่กำหนดเองเพื่อรับอินสแตนซ์คลาสของคลาสโดยอัตโนมัติ
// A pair of arbitrary case classes
case class Foo(i : Int, s : String)
case class Bar(b : Boolean, s : String, d : Double)
// Publish their `HListIso`'s
implicit def fooIso = Iso.hlist(Foo.apply _, Foo.unapply _)
implicit def barIso = Iso.hlist(Bar.apply _, Bar.unapply _)
// And now they're monoids ...
implicitly[Monoid[Foo]]
val f = Foo(13, "foo") |+| Foo(23, "bar")
assert(f == Foo(36, "foobar"))
implicitly[Monoid[Bar]]
val b = Bar(true, "foo", 1.0) |+| Bar(false, "bar", 3.0)
assert(b == Bar(true, "foobar", 4.0))
ไม่มีการวนซ้ำแบบรันไทม์ที่นี่ แต่มีการทำซ้ำซึ่งการใช้HLists
(หรือโครงสร้างที่เทียบเท่า) สามารถกำจัดได้ แน่นอนถ้าความอดทนของคุณสำหรับหม้อไอน้ำซ้ำ ๆ สูงคุณสามารถได้รับผลลัพธ์เดียวกันโดยการเขียนการใช้งานที่หลากหลายสำหรับแต่ละรูปร่างที่คุณสนใจ
ในคำถามที่สามคุณถามว่า "... ถ้าฟังก์ชั่น f ที่คุณจับคู่กับ hlist นั้นสามัญมากจนยอมรับองค์ประกอบทั้งหมด ... ทำไมไม่ใช้มันผ่าน productIterator.map?" หากฟังก์ชั่นที่คุณแมปผ่าน HList นั้นเป็นแบบฟอร์มจริงๆAny => T
แล้วการจับคู่ผ่านproductIterator
จะให้บริการคุณได้อย่างสมบูรณ์แบบ แต่Any => T
โดยทั่วไปแล้วฟังก์ชั่นของแบบฟอร์มนั้นไม่น่าสนใจ (อย่างน้อยพวกมันก็ไม่ได้เว้นแต่ว่าพวกเขาจะพิมพ์ภายใน) shapeless ให้รูปแบบของค่าฟังก์ชัน polymorphic ซึ่งทำให้คอมไพเลอร์สามารถเลือกเคสเฉพาะประเภทในแบบที่คุณสงสัย ตัวอย่างเช่น
// size is a function from values of arbitrary type to a 'size' which is
// defined via type specific cases
object size extends Poly1 {
implicit def default[T] = at[T](t => 1)
implicit def caseString = at[String](_.length)
implicit def caseList[T] = at[List[T]](_.length)
}
scala> val l = 23 :: "foo" :: List('a', 'b') :: true :: HNil
l: Int :: String :: List[Char] :: Boolean :: HNil =
23 :: foo :: List(a, b) :: true :: HNil
scala> (l map size).toList
res1: List[Int] = List(1, 3, 2, 1)
สำหรับคำถามที่สี่เกี่ยวกับการป้อนข้อมูลของผู้ใช้มีสองกรณีที่ต้องพิจารณา สิ่งแรกคือสถานการณ์ที่เราสามารถสร้างบริบทซึ่งรับประกันได้ว่าเงื่อนไขแบบสแตติกที่รู้จักนั้นจะได้รับ ในสถานการณ์ประเภทนี้เป็นไปได้อย่างสมบูรณ์แบบที่จะใช้เทคนิคที่ไม่มีรูปแบบ แต่เห็นได้ชัดว่ามีเงื่อนไขว่าหากเงื่อนไขแบบสแตติกไม่ได้มาที่รันไทม์ดังนั้นเราต้องทำตามเส้นทางอื่น ไม่น่าแปลกใจซึ่งหมายความว่าวิธีการที่มีความไวต่อเงื่อนไขแบบไดนามิกต้องให้ผลลัพธ์ที่เป็นตัวเลือก นี่คือตัวอย่างการใช้HList
s
trait Fruit
case class Apple() extends Fruit
case class Pear() extends Fruit
type FFFF = Fruit :: Fruit :: Fruit :: Fruit :: HNil
type APAP = Apple :: Pear :: Apple :: Pear :: HNil
val a : Apple = Apple()
val p : Pear = Pear()
val l = List(a, p, a, p) // Inferred type is List[Fruit]
ประเภทของl
ไม่จับภาพความยาวของรายการหรือประเภทองค์ประกอบที่แม่นยำ อย่างไรก็ตามถ้าเราคาดหวังว่ามันจะมีรูปแบบเฉพาะ (เช่นถ้ามันควรจะสอดคล้องกับที่รู้จักกันบางสคีคงที่) จากนั้นเราสามารถพยายามที่จะสร้างความจริงนั้นและดำเนินการตามนั้น
scala> import Traversables._
import Traversables._
scala> val apap = l.toHList[Apple :: Pear :: Apple :: Pear :: HNil]
res0: Option[Apple :: Pear :: Apple :: Pear :: HNil] =
Some(Apple() :: Pear() :: Apple() :: Pear() :: HNil)
scala> apap.map(_.tail.head)
res1: Option[Pear] = Some(Pear())
มีสถานการณ์อื่น ๆ ที่เราอาจไม่สนใจเกี่ยวกับความยาวจริงของรายการที่กำหนดนอกเหนือจากนั้นจะมีความยาวเท่ากับรายการอื่น ๆ อีกครั้งนี่คือสิ่งที่ไม่มีรูปแบบรองรับทั้งแบบสแตติกและในบริบทแบบสแตติก / ไดนามิกดังกล่าวข้างต้น ดูที่นี่สำหรับตัวอย่างเพิ่มเติม
มันเป็นความจริงในขณะที่คุณสังเกตเห็นว่ากลไกเหล่านี้ทั้งหมดต้องการข้อมูลประเภทสแตติกเพื่อให้สามารถใช้ได้อย่างน้อยมีเงื่อนไขและดูเหมือนว่าจะแยกเทคนิคเหล่านี้ออกจากการใช้งานในสภาพแวดล้อมแบบไดนามิกสมบูรณ์ขับเคลื่อนโดยข้อมูลที่ไม่ได้พิมพ์ แต่ด้วยการถือกำเนิดของการสนับสนุนสำหรับการรวบรวมรันไทม์เป็นส่วนประกอบของ Scala สะท้อนใน 2.10 ที่แม้นี้ไม่เป็นอุปสรรคผ่านไปไม่ได้ ... เราสามารถใช้การรวบรวมรันไทม์เพื่อให้รูปแบบของการแสดงละครที่มีน้ำหนักเบาและมีการพิมพ์แบบคงที่เราดำเนินการที่รันไทม์ ในการตอบสนองต่อข้อมูลแบบไดนามิก: ข้อความที่ตัดตอนมาจากด้านล่างก่อนหน้า ... ไปตามลิงก์สำหรับตัวอย่างเต็ม
val t1 : (Any, Any) = (23, "foo") // Specific element types erased
val t2 : (Any, Any) = (true, 2.0) // Specific element types erased
// Type class instances selected on static type at runtime!
val c1 = stagedConsumeTuple(t1) // Uses intString instance
assert(c1 == "23foo")
val c2 = stagedConsumeTuple(t2) // Uses booleanDouble instance
assert(c2 == "+2.0")
ฉันแน่ใจว่า@PLT_Boratจะมีบางอย่างที่จะพูดเกี่ยวกับเรื่องนี้เนื่องจากความเห็นของปราชญ์เกี่ยวกับภาษาการเขียนโปรแกรมที่พึ่งพาได้ ;-)