Dotty: Change syntax of given whitebox macros

Created on 26 Mar 2020  Â·  17Comments  Â·  Source: lampepfl/dotty

Status quo

inline def whiteBox1 <: T = ...
inline given whiteBox2 as _ <: T = ...

Critique

  • Syntax is not consistent
  • You could argue that the first syntax is too quiet and also misleading since at first glance whiteBox1 is in the position where you would expect to see a type.
  • _ <: T is technically wrong (or, rather, old style) since it suggests a lambda. We are after an existential instead.

Proposal

Write ? <: T (analogous to a wildcard type argument) to express both forms of whitebox macros. I.e.

inline def whiteBox1: ? <: T = ...
inline given whiteBox2 as ? <: T = ...

Opinions?

metaprogramming language enhancement

Most helpful comment

The problem is that:

inline def foo : ? <: String

is very different semantically from:

inline def foo : List[? <: String]

The latter is not whitebox at all, even though it uses a very similar syntax.

All 17 comments

? <: T makes me think I can put something other than ? on the left which is not the case. If we need to change the syntax I would rather use an extra keyword to denote whiteboxity (I think whitebox is fine since it's well-established Scala jargon).

Or transparent?

I actually like the ? <: T syntax - it expresses the "unknown subtype of T" concept well.

Reg. trying to replace ? with another type - to me doing that would result in something that reads like nonsense, so I'm not sure if very many people will try to do that. Consider:

inline def foo : ? <: String === "define an inline foo that returns some subtype of String"
inline def foo : "a" <: String == "define an inline foo that returns "a", which is a subtype of String"

The problem is that:

inline def foo : ? <: String

is very different semantically from:

inline def foo : List[? <: String]

The latter is not whitebox at all, even though it uses a very similar syntax.

Hm, you may be right and it may be too cute. How would the keyword syntax look? Like this?

inline whitebox def foo : String = …

whitebox inline def I'd say, whitebox is a modifier of inline.

The part that I am slightly miffed about is that we would lose the "this is not an actual type annotation" suggestion that the current syntax foo <: String expresses. Might not be a big loss, all things considered.

I'd go for transparent inline. whitebox is too much expert speak. Also, it works better in conjunction with blackbox but we do not have that one as a modifier.

How about ?

inline def whiteBox1 : ?String
inline given whiteBox2 as ?T = ...

This was propose and rejected for nullable types.

Finally, I have always thought that this feature deserved a flag. We currently have no way to know if an inline method is whitebox or not after it is typed.

Scala 2 accidentally treats an empty refinement as "do not widen" indicator. Could we make that official and use it here,

inline def foo : String {} ...

We should have a syntax where not widening would not happen by mistake.

I am not sure about the flag. We are currently trying hard to reduce the number of flags that we pickle. Even without a flag., there could be a robust predicate on symbols indicating whether an inline function is blackbox or whitebox Namely, an inline function f such as

inline def f(): T = rhs

is blackbox if rhs is of the form E: T1 where T =:= T1 (we only need to test T <:< T1, the other direction holds anyway). If that's the case, the expansion of f cannot change the type, which is what
characterizes a blackbox macro. Now it's true that if someone writes

transparent inline def f(): T = E: T

that also gives a blackbox macro according to our definition, despite the transparent. But I'd argue that's OK, and even desirable: we capture that way the semantics of a blackbox macro, not the syntactic representation.

We are currently trying hard to reduce the number of flags that we pickle.

Why though? That sounds like a useless target metric. What's important is to pickle the semantics of the language, and not a particular encoding of the language. It should add simple as possible, sure, but not simpler.

You can argue that the semantics of blackbox macros is a whitebox macro with an ascription. But then that's also how you have to specify the language. And that means that you cannot explain blackbox macros without explaining whitebox macros. That doesn't seem like a good deal to me.

Note that we will need as a flag (or at least in the TASTy file) for #7825.

Note that we will need as a flag (or at least in the TASTy file) for #7825.

What I had in mind was a method isBlackboxInline on symbols that implements the logic I outlined before. That method should give valid results for methods coming from Tasty as well. So we would not need a flag.

Here's the ruleset:

  • A blackbox macro is a macro that on inline expansion keeps its type
  • A normal inline method always keeps its type since its inline expansion is ascripted with its result type. That's a desugaring step, not relevant for Tasty.
  • A transparent inline does not get ascripted, so it _might be_ a whitebox macro (and probably will be, in most cases).

That means we can treat whitebox/blackboxity without having to change the Tasty format. The Tasty format remains smaller, which is a win in my book.

It also removes some magic from the inlining. Otherwise we'd have to specify differences of inline expansion for blackbox and for whitebox macros. One gets its type widened on expansion, the other does not. By pushing it all into desugaring (ascribe or not) we avoid this added complexity.

Was this page helpful?
0 / 5 - 0 ratings