Is erased on the horizon to be inlcuded as non experimental?

I have a couple of apis where I’ve used erased, but the effort of keeping that codebase at nightly releases is very annoying, metals often doesn’t like it.

At this point I’m more inclined to drop it than to continue waiting, but if the prospect of the feature making into the language is close enough I might wait. What’s the current status on it?

I personally would like to see this in soon. Maybe another 3 months? We have to convince the SIP committee that it’s worth it. So if you have use cases to share that would help.

3.1. is about to be released with better support for experimental features. In particular, you will likely be able to mark an API as experimental with @experimental and use erased in that API. We are still trying to work out the details. See #13394 and #13396 in the Dotty repo.

5 Likes

I just found out about erased and I have a question: isn’t it always possible and therefore wouldn’t it be better to automatically figure out which terms can be erased and go ahead and erase them instead of having the users manually mark terms by using a new keyword?

One argument against is that it would probably be a nightmare for maintaining binary compatibility.

1 Like

Hmm, I’m not sure I have really valid case, but maybe I’m using it as palliative for the lack of a “better” feature?

Example 1:

I’m using it as temporary store for a complicated calculated type (in a library for generic records)

erased trait TypeWitness[N]:
  type T = N
erased given [T]: TypeWitness[T] = null
...
def renameField(field: String & Singleton, to: String & Singleton)
          (using erased w: TypeWitness[IndexOf[TupleMap[Tup, @@[String & Singleton, Any], FieldName], field.type]])
          (using i: ValueOf[w.T]): Tuple.Concat[Tuple.Take[Tup, w.T], (to.type @@ FieldType[Tuple.Elem[Tup, w.T]]) *: Tuple.Drop[Tup, S[w.T]]]

In the above example, w is used 4 times in the following types, copy pasting all that type would not be desirable (furthermore I don’t know if the compiler caches the calculation of this type, which might not be cheap).
We could argue that the above is not a valid usage of erased and what I really wanted here is being able to declare an auxiliary type.

Example 2:

While using vulkan bindings, I have a wrapper layer that gives me a modular structure and methods on top of essentially C methods (so the api feels more like the c++ or rust one), and I have to make decisions about allocations of structs all the time. I abstracted this with

trait Allocator[S]:
  type Buffer <: CustomBuffer[_] // ideally I should be able to not have to put an upper bound, but the compiler crashes if I don't when extending
  inline def stackCalloc(stack: MemoryStack): S
  inline def stackMalloc(stack: MemoryStack): S
  inline def calloc(): S
  inline def malloc(): S
  transparent inline def stackCallocBuffer(stack: MemoryStack, capacity: Int): Buffer
  transparent inline def stackMallocBuffer(stack: MemoryStack, capacity: Int): Buffer
  transparent inline def callocBuffer(capacity: Int): Buffer
  transparent inline def mallocBuffer(capacity: Int): Buffer

and then tons of methods are inline and take a AllocMethod and an implicit Allocator.
In this second exampe, I use erased to make sure that a reference to Allocator is never actually popping up in the bytecode, since it should only ever be inlining (and this happened a couple of times when I accidentally stored the allocator to a variable that wasn’t inline’d itself).
Same thing with a Managed[T] that I use essentially auto freeing things once they are out of scope.

In this second example we could argue I’m abusing erased and I should just trust the compiler.

@odersky IIUC, you’re saying that erased terms is not losing its experimental status in 3.1, and the earliest it will became non-experimental is 3.2. Is that right?

Will all experimental features benefit from this improvement? E.g., can I use optional braces for expressions inside a class marked @experimental?

No, it’s impossible to do without a whole-program analysis, which would make the erasure behavior very brittle and hard to predict. Someone doing something fishy with their supposedly-erased value in one library would cause code on the other side of the program to stop erasing its values, degrading performance in unpredictable ways.

The dependent programming community has learned this lesson the hard way already. Recent related work about it:

They propose explicit erasure annotations and they do also propose an erasure inference algorithm, but such inference will have the same anti-modular properties I mentioned above.

1 Like

Yes, that’s the idea.

1 Like

Yes, the earliest would be 3.2. But minor releases will happen more often than in the past. 3.1 will be released this week, about 3 months after 3.0. 3.2 might be released in another 3 months. All minor releases are backwards binary compatible, so upgrading is not the same issue as it was in Scala 2.x.

3 Likes

They should be interesting for “evidence” given parameters like in IterableOnceOps#toMap:

def toMap[K, V](implicit ev: A <:< (K, V)): immutable.Map[K, V] =
    immutable.Map.from(this.asInstanceOf[IterableOnce[(K, V)]]

Here (correct me if I’m wrong), ev can be marked as erased because it is only required as evidence at compile-time.

The type <:< acts as an implicit conversion in scope (at least in scala 2) and when converting, the method (apply) is not inline so the reference needs to be used (that’s because it extends regular function).