Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scala 3: find a good way to bind functions with implicit parameters using new Scala 3 features #1897

Open
neko-kai opened this issue Feb 10, 2023 · 0 comments

Comments

@neko-kai
Copy link
Member

Currently it's somewhat hard to bind functions with implicit parameters using ModuleDefDSL. We can't bind them like ordinary functions:

final case class Description[T](description: String)
final case class X(s: String)

def makeX()(implicit desc: Description[X]): X = new X(desc.description)

object module extends ModuleDef {
  make[Description[X]].fromValue(Description("X"))
  make[X].from(makeX _) // error: no implicit value Description[X]
}

Because implicit parameters are resolved eagerly the moment we refer to the function value makeX _ - they are not part of the type signature of the function value: val f: () => X = makeX _ // no Description[X] parameter. The two ways to work around this currently are to:

  1. pass the implicit parameters explicitly:
make[X].from((d: Description[X]) => makeX()(d))
  1. And (preferred) to wrap the function into a class and use automatic class construction feature of the DSL:
object X {
  final class Resource(implicit desc: Description[X])
    extends Lifecycle.LiftF[Identity, X](
      makeX
    )
}

make[X].fromResource[X.Resource]

This is the main use case of helper classes Lifecycle.LiftF and Lifecycle.Of - to create class-based wrappers for functions that require implicit parameters (such as typeclass instances), to workaround the limitation of Scala 2 that implicit functions are unrepresentable - to improve ergonomics of adding them to the object graph.

Scala 3

However, Scala 3 now makes implicit functions representable which could allow us to solve this problem directly. Unfortunately the easiest way to support this - using overloading - doesn't work - lampepfl/dotty-feature-requests#150, but perhaps we could implement this using a macro - using a signature like def from[I <: T](f: Nothing ?=> Functoid[I]): AfterFrom to obtain a tree such as (nothing: Nothing) ?=> makeX()(nothing) and then rewriting the tree by substituting the Nothing placeholder with the correct types for arguments taken out of makeX type signature: (desc: Description[X]) => makeX()(using desc)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant