【发布时间】:2021-06-03 06:12:24
【问题描述】:
我很难理解为什么隐式解析在以下情况下不起作用。希望您对这个问题有一些见解。
trait Jumper[F[_]] {
def jump[A](a: F[A]): A
}
implicit def conversion[F[_]: Jumper, A](a: F[A]): A = implicitly[Jumper[F]].jump(a)
implicit val optionJumps: Jumper[Option] = new Jumper[Option] {
override def jump[A](a: Option[A]): A = a.get
}
implicit val sequenceJumps: Jumper[Seq] = new Jumper[Seq] {
override def jump[A](a: Seq[A]): A = a.head
}
// What I would hope would enable chaining effects (i.e. composing Jumpers)
implicit def composed[F[_]: Jumper, G[_]: Jumper]: Jumper[({ type f[x] = F[G[x]] })#f] =
new Jumper[({ type f[x] = F[G[x]] })#f] {
override def jump[A](a: F[G[A]]): A = implicitly[Jumper[G]].jump(implicitly[Jumper[F]].jump(a))
}
case class AddressPerson(city: String, road: String, number: Int, country: String)
val addressPerson: AddressPerson = ???
addressPerson.city // obviously works, no implicit conversion occurs
val addressPersonOpt: Option[AddressPerson] = ???
addressPersonOpt.city // works thanks to the implicit conversion and optionJumps
val addressPersonSeq: Seq[AddressPerson] = ???
addressPersonSeq.city // works thanks to the implicit conversion and sequenceJumps
val addressPersonSeqOpt: Option[Seq[AddressPerson]] = ???
addressPersonSeqOpt.city // does not work! I wanted this to work out of the box via `composed`
// error is: value city is not a member of Option[Seq[AddressPerson]]
// addressPersonSeqOpt.city
// ^
// however, if I define type alias
type Alias[Y] = Option[Seq[Y]]
val addressPersonSeqOpt: Alias[AddressPerson] = ???
addressPersonSeqOpt.city // works
有人知道这里发生了什么吗?为什么隐式解析无法理解我们可以将Option[Seq[Y]]转换为Y?
#########
也许说我可以用下面的不同代码克服上面的困难是相关的,但我仍然想知道为什么组合不起作用。
因此,或者,我们可以删除“composed”,而是像这样显式声明一个新的隐式转换:
implicit def composedImplicitConversion[F[_]: Jumper, G[_]: Jumper, A](value: F[G[A]]): A =
implicitly[Jumper[G]].jump(implicitly[Jumper[F]].jump(value))
现在它可以工作了:
val addressPersonSeqOpt: Option[Seq[AddressPerson]] = ???
addressPersonSeqOpt.city // it works!
最后,在我讨厌使用这样的隐式转换之前,请考虑这是一个假设的问题。
我不打算在运行时调用addressPersonOpt.city!
如果您必须知道,它可以与宏一起使用。
我只需要这种隐式转换才能工作,这样我就可以传递足够的函数签名并仅使用宏对其进行分析(与函数对应的树)..
【问题讨论】:
-
请不要这样做。您只会使代码更难理解、更难调试、更难更改和更难编译。您还隐藏了可能导致代码崩溃的不安全操作。
-
这是实现我想要的唯一方法。但不用担心,显然目的是不能在运行时调用
addressPersonSeq.city。这个隐含的决议实际上永远不会运行!它位于一个宏的后面,我需要向它提供像(f: Option[AddressPerson]) => K这样的函数。但是这些函数永远不会被调用。宏只是分析函数树。 -
不确定您要做什么,但是必须使用一些隐式转换编写一些代码,然后实际上由宏重写只会使所有 “更难”我之前对“不可能”的评论。
-
我正在尝试做一些类似于 Chimney 的 dsl 的事情。隐式转换永远不会重写。它们从未被使用过,它们从未被调用过。想象一下。
def foo(f: K => V) = macro MacroImpl其中K可能是Option[Seq[AddressPerson]]。我图书馆的客户调用foo,它必须提供f。但是f永远不会被调用。并且这种隐式转换是隐藏的。只能显式导入。然后以完全不涉及转换或隐式的方式分析与f对应的语法树。你还会对此有异议吗? -
这基本上是为了让我图书馆的用户可以做
foo(_.city)。或者foo(optionSeqAddress => optionSeqAddress.city)。这是非常用户友好的。同样,用户提供的函数永远不会被调用。某些元信息将仅由宏提取。
标签: scala implicit-conversion implicit higher-kinded-types