【问题标题】:Why is implicit resolution failing?为什么隐式解析失败?
【发布时间】: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


【解决方案1】:

您需要向编译器提示以找出什么是A 和什么是F,如果您不创建类型别名,编译器假定FOption[_] 和A @987654325 @,这就是为什么隐式转换不起作用的原因,因为转换后代码没有编译。

另一方面,当您使用类型别名时,编译器很清楚 FOption[Seq[_]]AAddressPerson

【讨论】:

  • 有道理。这也是我的猜测。你对如何去做这件事有什么建议吗?此外,虽然它是有道理的,但这是否意味着我最后解释的替代方案(使用 implicit def composedImplicitConversion )也应该有这个问题,因此不能编译?
  • 类型别名在编译时是相关的,您通过使用它们向编译器提供明确的类型信息。你是说我的 F 是别名,所以编译器不必猜测 F 是什么。
猜你喜欢
  • 2019-09-06
  • 1970-01-01
  • 2020-09-09
  • 2021-03-02
  • 1970-01-01
  • 1970-01-01
  • 2015-10-24
  • 1970-01-01
  • 2018-05-18
相关资源
最近更新 更多