我正在努力学习无形,但我发现无形代码真的很难理解。
所以我从youtube上的演讲中得到了这个代码示例。
https://www.youtube.com/watch?v=JKaCCYZYBWo
任何人都可以向我解释发生了什么(一步一步)。我发现最困难的事情是一切都是隐含的,因此很难追踪代码......
另外,请指出一些可以帮助我理解这样的代码的资源。每当我遇到有这么多含义的代码时,我觉得我甚至不知道Scala。
import shapeless._
sealed trait Diff[A]
final case class Identical[A](value: A) extends Diff[A]
final case class Different[A](left : A, right : A) extends Diff[A]
object Diff {
def apply[A](left : A, right : A) : Diff[A] = {
if (left == right) Identical(left)
else Different(left, right)
}
}
trait SimpleDelta[R <: HList] extends DepFn2[R, R] {
type Out <: HList
}
object SimpleDelta {
type Aux[I <: HList, O <: HList] = SimpleDelta[I]{type Out = O}
implicit def hnilDelta: Aux[HNil, HNil] = new SimpleDelta[HNil] {
type Out = HNil
def apply(l : HNil, r: HNil) : Out = HNil
}
implicit def hconsDelta[H, T <: HList, DT <: HList](implicit tailDelta: Aux[T, DT])
: Aux[H::T, Diff[H] :: DT] = new SimpleDelta[H :: T] {
type Out = Diff[H] :: DT
def apply(l : H :: T, r: H :: T) : Out =
Diff(l.head, r.head) :: tailDelta(l.tail, r.tail)
}
def apply[A, R <: HList](l : A, r: A)
(implicit genA: Generic.Aux[A, R], delta: SimpleDelta[R]) : delta.Out =
delta(genA.to(l), genA.to(r))
}
case class Address(number: Int, street: String, city: String)
case class Character(name: String, age: Int, address: Address)
val homer = Character("Homer Simpson", 42, Address(742, "Evergreen Terrace", "SpringField"))
val ned = Character("Ned Flanders", 42, Address(744, "Evergreen Terrace", "SpringField"))
SimpleDelta(homer, ned)
答案 0 :(得分:2)
HList
s)完成的,它可以作为案例类的通用表示。无形提供了在案例类和通用HList
表示之间进行转换的基础结构。请注意,在此示例中,差异不是递归计算的,即任何嵌套的case类实例都将以原子方式进行比较。Diff
特征和伴侣对象是相当不言自明的。SimpleDelta
特征定义了一个带有两个参数(DepFn2
)的函数。签名是(R, R) => Out
。此外,声明了对结果类型的约束:Out <: HList
。这意味着,对于SimpleDelta
的所有子类型,类型成员Out
必须是HList
的子类型。SimpleDelta
定义了类型Aux
。您可以在网络上找到有关Aux模式的更多信息,例如here。简而言之,它充当SimpleDelta
特征的别名,允许使用类型参数表达Out
类型成员。SimpleDelta
对象定义了两个隐式方法hnilDelta
和hconsDelta
。这些方法中的每一个都代表HList
,HNil
和HCons
的可能构造之一。 HList
A :: B :: HNil
也可以理解为::(A, ::(B, HNil))
或HCons(A, HCons(B, HNil))
。使用这两种方法,可以递归地解构和处理输入HList
。根据这些方法的返回类型,编译器知道在遇到SimpleDelta[R]
类型的隐式参数时隐式解析哪一个:当R
被推断为HNil
时,它将调用hnilDelta
;如果某些类型R
和Head :: Tail
已将HCons(Head, Tail)
推断为Head
(或Tail
),则会调用hconsDelta
。hnilDelta
方法确定两个空HList
之间的差值(HNil
为空HList
)。
Out
类型成员设置为HNil
,这是HNil
对象的类型。结果是HNil
,因为没有区别。Aux[HNil, HNil]
。如上所述,这是SimpleDelta[HNil] { type Out = HNil }
的别名。它告诉编译器在遇到类型为SimpleDelta[R]
的隐式参数时调用此方法,并且R
已被推断为HNil
。hconsDelta
方法确定两个非空HList
s之间的差值,即两个由HLists
类型的头元素和尾部组成的H
类型列表T
。
DT
表示代表尾部列表增量的HList
子类型。tailDelta :Aux[T, DT]
。它表明该方法必须能够确定两个列表l
和r
的尾部之间的差值:tailDelta(l.tail, r.tail)
。Aux[H::T, Diff[H] :: DT]
。如上所述,这是SimpleDelta[H::T] { type Out = Diff[H] :: DT }
的别名。它告诉编译器在遇到类型为SimpleDelta[R]
的隐式参数时调用此方法,并且R
已针对某些类型H::T
和{{1}推断为H
}。T
设置为Out
,这是产生差异的Diff[H] :: DT
类型。HList
方法确定两个头元素之间的差异,并将其预先设置为尾部列表之间的差异,尾部列表使用隐式参数apply
计算(见上文)。tailDelta
方法负责将apply
类型的实例l
和r
转换为其通用A
表示形式,并调用{{1}在这些HList
上计算差异。
SimpleDelta
是HList
的通用R
表示形式。隐式参数HList
表示该方法需要在A
和genA: Generic.Aux[A, R]
之间进行转换。A
。这是计算R
和delta: SimpleDelta[R]
的两个通用HList
表示之间的差异所必需的。l
。请注意,此处使用了依赖类型,因为r
参数的实际delta.Out
类型成员未知 - 它取决于实际类型Out
及其delta
表示{ {1}}。编译A
时会发生什么?
HList
。R
和SimpleDelta(homer, ned)
的匹配值。SimpleDelta.apply[Character, R](homer, ned)
提供隐式genA: Generic.Aux[Character, R]
。这个机制相当复杂,超出了这个问题的范围,但相关的是类型delta: SimpleDelta[R]
的通用Generic.Aux[A, R]
表示是A
,因此编译器可以推断出类型HList
到Character
。这反过来意味着类型变量String :: Int :: Address :: HNil
可以推断为genA
。Generic.Aux[Character, String :: Int :: Address :: HNil]
已知,编译器将尝试解析参数R
的隐含值,即String :: Int :: Address :: HNil
。当前作用域不包含具有此返回类型的任何隐式方法或具有此类型的值,但编译器还将查询参数类型R
的隐式作用域,其中包括伴随对象{{1} (请参阅Scala文档中的Implicit Parameters部分)。delta: SimpleDelta[R]
对象中搜索提供类型为SimpleDelta[String :: Int :: Address :: HNil]
的对象的值或方法。唯一匹配的方法是SimpleDelta
,因此编译器将参数值SimpleDelta
连接到此方法的调用。请注意,SimpleDelta
会分解为头尾:SimpleDelta[String :: Int :: Address :: HNil]
。hconsDelta
参数,编译器将继续查找匹配的隐式方法。这样,delta
以递归方式解构,直到只剩下HList
并调用hconsDelta[String, Int :: Address :: HNil, DT](implicit tailDelta: Aux[Int :: Address :: HNil, DT])
。tailDelta
类型变量将推断为HList
,返回类型为HNil
到hnilDelta
。因此,DT
的返回类型推断为Diff[Int] :: Diff[Address] :: HNil
。