Skip to content

Can't Abstract Over Functions And Context Functions #15901

Open
@adamgfraser

Description

@adamgfraser

In some cases it is necessary to abstract over context functions and ordinary functions.

For example, consider a function that would eliminate a dependency on a capability Foo. We can write this in Scala 3 like this:

trait Foo

def provideContextFunction[A](f: Foo ?=> A): A =
  f(using new Foo {})

def contextFunction(using Foo): Int =
  42

provideContextFunction(contextFunction) // okay

However, we might also want to be able to eliminate an explicit dependency on a capability Foo. This could be particular important for compatibility with Scala 2 where functions that depend on implicit parameters are ordinary functions since context functions do not exist.

For instance, this will not compile:

provideContextFunction(implicit foo => 42) // does not compile

We can do this explicitly:

def provideFunction[A](f: Foo => A): A =
  f(new Foo {})

def function(foo: Foo): Int =
  42

provideFunction(function) // okay
provideFunction(implicit foo => 42) // okay

However, we can't mix and match these:

// provideFunction(contextFunction) does not compile
// provideContextFunction(function) does not eliminate requirement

We also can't give provideFunction and provideContextFunction the same name since they erase to the same type.

This makes it impossible to write an operator that eliminates the dependency on a capability that can be used consistently across Scala 2 and Scala 3.

What we would like to be able to do is write something like:

def provide[A](f: FunctionLike[Foo, A]): A =
  f.apply(new Foo {})

provide(contextFunction) // okay
provide(function) // okay

This is just pseudocode but assumes there is some common super type of functions and context functions that describes something that an argument can be applied to. It doesn't appear that this currently exists and apologies if I missed it.

Maybe there is another solution to this as well such as converting lambdas of the form implicit x => y into context functions but it seems like Scala 2 is still going to be used commercially for a while so it will be important for library adoption of capabilities to be able to eliminate them in a way that works across Scala versions.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions