ในการใส่สิ่งต่าง ๆ ในบริบท: คำตอบนี้โพสต์ครั้งแรกในหัวข้ออื่น คุณเห็นที่นี่เพราะมีการรวมสองเธรด คำแถลงคำถามในหัวข้อดังกล่าวมีดังนี้:
วิธีแก้ไขคำจำกัดความประเภทนี้: บริสุทธิ์ [({type? [a] = (R, a)}) #?]?
อะไรคือสาเหตุของการใช้การก่อสร้างดังกล่าว?
Snipped มาจากไลบรารี scalaz:
trait Pure[P[_]] {
def pure[A](a: => A): P[A]
}
object Pure {
import Scalaz._
//...
implicit def Tuple2Pure[R: Zero]: Pure[({type ?[a]=(R, a)})#?] = new Pure[({type ?[a]=(R, a)})#?] {
def pure[A](a: => A) = (Ø, a)
}
//...
}
ตอบ:
trait Pure[P[_]] {
def pure[A](a: => A): P[A]
}
ขีดเส้นใต้หนึ่งในกล่องหลังจากP
นั้นก็หมายความว่ามันเป็นตัวสร้างประเภทใช้เวลาหนึ่งประเภทและส่งกลับประเภทอื่น ตัวอย่างประเภทของตัวสร้างประเภทนี้: List
, Option
.
ให้ชนิดคอนกรีตและมันจะช่วยให้คุณอีกประเภทคอนกรีต ให้และมันจะช่วยให้คุณ เป็นต้นList
Int
List[Int]
List
String
List[String]
ดังนั้นList
, Option
ได้รับการพิจารณาให้เป็นฟังก์ชั่นระดับประเภทของ arity 1. * -> *
อย่างเป็นทางการเราพูดว่าพวกเขามีชนิด เครื่องหมายดอกจันแสดงถึงชนิด
ตอนนี้Tuple2[_, _]
เป็นตัวสร้างชนิดที่มีชนิด(*, *) -> *
เช่นคุณต้องให้มันเป็นสองประเภทเพื่อรับชนิดใหม่
เนื่องจากลายเซ็นไม่ตรงกับคุณไม่สามารถทดแทนสำหรับTuple2
P
สิ่งที่คุณต้องทำคือนำไปใช้ Tuple2
กับข้อโต้แย้งข้อใดข้อหนึ่งของมันซึ่งจะให้ตัวสร้างประเภทกับ* -> *
เราและเราสามารถทดแทนมันP
ได้
น่าเสียดายที่สกาล่าไม่มีไวยากรณ์พิเศษสำหรับการใช้งานตัวสร้างแบบบางส่วนและดังนั้นเราจึงต้องหันไปใช้ความน่าพิศวงที่เรียกว่าแลมบ์ดาชนิด (สิ่งที่คุณมีในตัวอย่างของคุณ) พวกมันถูกเรียกว่าเพราะมันคล้ายกับการแสดงออกแลมบ์ดาที่มีอยู่ในระดับคุณค่า
ตัวอย่างต่อไปนี้อาจช่วยได้:
// VALUE LEVEL
// foo has signature: (String, String) => String
scala> def foo(x: String, y: String): String = x + " " + y
foo: (x: String, y: String)String
// world wants a parameter of type String => String
scala> def world(f: String => String): String = f("world")
world: (f: String => String)String
// So we use a lambda expression that partially applies foo on one parameter
// to yield a value of type String => String
scala> world(x => foo("hello", x))
res0: String = hello world
// TYPE LEVEL
// Foo has a kind (*, *) -> *
scala> type Foo[A, B] = Map[A, B]
defined type alias Foo
// World wants a parameter of kind * -> *
scala> type World[M[_]] = M[Int]
defined type alias World
// So we use a lambda lambda that partially applies Foo on one parameter
// to yield a type of kind * -> *
scala> type X[A] = World[({ type M[A] = Foo[String, A] })#M]
defined type alias X
// Test the equality of two types. (If this compiles, it means they're equal.)
scala> implicitly[X[Int] =:= Foo[String, Int]]
res2: =:=[X[Int],Foo[String,Int]] = <function1>
แก้ไข:
ระดับค่าเพิ่มเติมและแนวระดับประเภท
// VALUE LEVEL
// Instead of a lambda, you can define a named function beforehand...
scala> val g: String => String = x => foo("hello", x)
g: String => String = <function1>
// ...and use it.
scala> world(g)
res3: String = hello world
// TYPE LEVEL
// Same applies at type level too.
scala> type G[A] = Foo[String, A]
defined type alias G
scala> implicitly[X =:= Foo[String, Int]]
res5: =:=[X,Foo[String,Int]] = <function1>
scala> type T = World[G]
defined type alias T
scala> implicitly[T =:= Foo[String, Int]]
res6: =:=[T,Foo[String,Int]] = <function1>
ในกรณีที่คุณนำเสนอพารามิเตอร์ประเภทR
เป็นท้องถิ่นเพื่อฟังก์ชั่นTuple2Pure
และดังนั้นคุณไม่สามารถกำหนดเพียงtype PartialTuple2[A] = Tuple2[R, A]
เพราะไม่มีสถานที่ที่คุณสามารถใส่คำพ้องความหมายว่า
เพื่อจัดการกับกรณีเช่นนี้ฉันใช้เคล็ดลับต่อไปนี้ที่ใช้ประโยชน์จากสมาชิกประเภท (หวังว่าตัวอย่างจะอธิบายได้ด้วยตนเอง)
scala> type Partial2[F[_, _], A] = {
| type Get[B] = F[A, B]
| }
defined type alias Partial2
scala> implicit def Tuple2Pure[R]: Pure[Partial2[Tuple2, R]#Get] = sys.error("")
Tuple2Pure: [R]=> Pure[[B](R, B)]