Some form of `contextalias Foo = context(Bar, Baz,...
# language-evolution
y
Some form of
contextalias Foo = context(Bar, Baz, ...)
thing for context parameters would be really nice! The alternative to this is a song-and-dance boilerplate-y ritual of having
Bar
and
Baz
be interfaces, having
interface Foo: Bar, Baz, ...
and then packing them together using interface delegation. Or of course one can repeatedly mention them together, but it'd be really nice if we can have that idea of a set of related context parameters that one might want to use (of course, it might be hard to name them, but perhaps it can only work with
context(_: Foo)
for now). This is nice too if you have transformers on
Baz
for instance since then you can do:
Copy code
context(_: Foo)
fun foo() = withTransformendBaz {
  otherFoo()
}
context(_: Foo)
fun otherFoo()
and thus
otherFoo
will get the initial
Bar
and the transformed
Baz
. Note that this example translates to:
Copy code
context(_: Bar, _: Baz)
fun foo() = withTransformendBaz {
  otherFoo()
}
context(_: Bar, _: Baz)
fun otherFoo()
and so it truly should be just a simple macro-expansion with no special semantics or anything.
p
I can certainly see the use for this. The downside being overuse of a common "context" on all functions, especially when most of the constituent parts may not be required. From the flip side, would you expect a function requiring the aliased context to be callable should the individual contexts be available? If so, you're also obscuring the true signature somewhat.
y
Yes I'd expect it to be callable given the elements. It feels like if the IDE just inline expands the signature to show the constituent elements then it should be fine. Overuse of a common context is a very real problem, but at least IDEA can very easily give a warning for that and even offer a refactoring to only keep the necessary elements. With the alternative mentioned of using interfaces, IDEA won't be able to give you any warnings for that because there's no indication that the interface exists solely for its elements.
d
cc @Alejandro Serrano.Mena
y
Since Alejandro is getting mentioned, a great example of this pattern is
IorRaise
in Arrow. It's currently composed of a
Raise
and an
accumulate
function, which could be its own standalone
Accumulate
interface. Then, you could have
contextalias IorRaise<E> = context(Raise<E>, Accumulate<E>)
. Another, much simpler example, is
ScopedRaiseAccumulate
which is just
RaiseAccumulate
+
CoroutineScope
. In fact,
RaiseAccumulate<E>
is really just
Raise<E>
+
Raise<Nel<E>>
, so
ScopedRaiseAccumulate<E>
is
context(Raise<E>, Raise<Nel<E>>, CoroutineScope)
. You can see how some of these interfaces are solely there to combine 2, while some also kind-of gain meaning on their own. Having this feature makes it really easy to fix the
recover
issue with Arrow builders because replacing
Raise
becomes really simple instead of having to recreate a
Raise
subclass. I'd be happy to expand more on that issue if you'd like