fir-lang/fir

Ergonomics or monomorphisation and trait objects

Opened this issue · 0 comments

osa1 commented

One of my favorite features from mainstream OOP languages like Dart and Java is you can use abstract class/interface types like any other type. You don't need type parameter lists, or special syntax for existentials/trait objects.

For example, I can define an abstract class for string types and some concrete types:

abstract class String { ... }

class Utf8String extends String { ... }

class AsciiString extends Utf8String { ... }

class Utf16String extends String { ... }

and then use the String type like any other concrete class type:

void sayHi(name: String) { ... }

class User {
    String name;
    ...
}

(Without monomorphisation this can be terrible for performance, and you have to seal all your performance critical classes so that no one will be able to add a subtype and ruin performance, but we don't have these problems in Fir.)

In Fir we already support this kind of thing via implicit trait objects, e.g.

trait Str:
    ...

type User:
    name: Str

fn sayHi(name: Str) = ...

and we also allow monomorphisation:

trait Str:
    ...

type User[S: Str]:
    name: S

fn sayHi[S: Str](name: S) = ...

Which is great, but if you want performance and use the monomorphisation syntax, then you will have to pass around those type parameters everywhere, it won't be as convenient as the polymoprhic version.

Ideally I want both versions to be equally convenient, and the user should choose one or the other based on runtime performance and binary size requirements.


One idea is we choose one of them as the default, and for the other variant we prefix the types with e.g. @, and make the type parameter list optional. Example:

trait Str: ...

type Utf8Str: ...

type AsciiStr: ...

type Utf16Str: ...

impl Str for Utf8String: ... 

impl Str for AsciiString: ... 

impl Str for Utf16String: ... 

type User:
    name: @Str  # trait object, there will be one User type in the program

# Will be monomorphised based on `Str` types passed.
fn sayHi(name: Str) = ...

(As usual, passing mono object as trait object boxes implicitly, passing trait object as mono creates a mono version for the trait object type)

In the IR we probably want to keep the type parameters explicitly.

In the front-end language, I'm not sure if we want to allow the users to specify the type lists. I don't know if there will be a use case for them without type applications.