softwaremill/quicklens

[Feature request, speculative] Allow arbitrary method "forwarding"

adampauls opened this issue · 4 comments

While writing #57, it occurred to me that it might be possible to do something general to "override" the behavior of existing methods on types. I think one can write a generic ForwardingFunctor that allows you to declare something like

trait OneTypeArgForwardingFunctor[F[_], T, M]

implicit def OptionGetForwardingFunctor[T]: OneTypeArgForwardingFunctor[Option, T, { def get: T}] = new ForwardingFunctor[Option, T, {def get: T}] {
  def get(ft: Option[T])(f: T => T): F[T] = Some(f(ft.get))
}

implicit def OptionGetOrElseForwardingFunctor[T]: OneTypeArgForwardingFunctor[Option, T, { def getOrElse(default: => T): T}] = new ForwardingFunctor[Option, T, {def get: T}] {
  def getOrElse(ft: Option[T], default: => T)(f: T => T): F[T] = Some(f(ft.getOrElse(default))
}

Then, when encountering a method call in a path, quicklens could first first call Context. inferImplicitValue(ForwardingFunctor[tpe.typeConstructor, tpe.typeArg, {method signature}]), where method signature is a structural type that matches the signature of method. If nothing is found, it can fallback to copies. Obviously the previous is not valid code but I hope you get my meaning.

This would allow client code to override the behavior of any existing method on any existing class without writing new macro code, so for example you could override Array.apply to act like at, and you could also allow people to call defs defined on case classes. I'm not entirely sure this will all work out, I'm still learning what the limits of macros are, and there's definitely some subtleties around higher-kinded types and mismatches in type parameters. Just a thought, would you be interested if I could work it out?

I'd happily take this on if you think this feature would be useful.

adamw commented

That would be a nice trick, however usage of structural types in Scala is discouraged. It's based on reflection, slow, non-graalvm-friendly etc. :)

Right, yes, I'm aware of that. IIUC though, the nice thing here is that the structural types would be compile-time only, just there to provide a way to get a handle on the implicit. No reflection would actually be called at runtime. I'm not sure if there's away to actually @compileTimeOnly those types, I can look into it.

I thought this through and it won't work like I expected.

adamw commented

Ah, well, always worth exploring :)