Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Record update syntax? #557

Open
marzipankaiser opened this issue Aug 27, 2024 · 3 comments
Open

Record update syntax? #557

marzipankaiser opened this issue Aug 27, 2024 · 3 comments
Labels
feature New feature or request requires-design

Comments

@marzipankaiser
Copy link
Contributor

Summary

It would be nice to have a syntax for record update in Effekt (the copying, immutable kind).

Proposal

Generate "update*" functions, so we can have:

record Foo(a: Int, b: Int)
val default = Foo(12,9)
val x = default.updateA(10)
println(x) // Foo(10,9)

Feel free to suggest better names/syntax...

Motivating use case

Some libraries may have multiple options, e.g. a regex library may have options for case sensitivity, multiline mode, ...

A default options record could then be passed or first updated using record update syntax (as common in e.g. Haskell).

Alternatives for this use case

  • keyword + default arguments (probably harder to implement?), also potentially needs: forwarding.
  • Instead of a record, effects could be used, but unless we have one single-operation effect per option, this does not solve the "set a single one" problem (but the equivalent "forward remaining operations" may be interesting).
@marzipankaiser marzipankaiser added feature New feature or request requires-design labels Aug 27, 2024
@b-studios
Copy link
Collaborator

Many languages use keyword + default arguments, as you mention. Why do you think those are difficult to implement?

@jiribenes
Copy link
Contributor

jiribenes commented Aug 27, 2024

There's also the TypeScript/Rust option: spreads which look like val x = Foo(a = 10, ..default), so the syntax for instantiating a record becomes

<record_inst> ::= <record_ident> `(` (<ident> `=` <expr> `,`? )* (`..` <expr>)? `)`
                | <record_ident> `(` (<expr> `,`? )* `)`

^ Allows just one spread in the tail position when using keyword arguments for the fields of the struct. The spread is used as a default for all otherwise uninitialised fields. The second variant allows only unnamed arguments and no spreads. Note that the first variant also encompasses a copying operation: val copy = Foo(..default).

So in this case, the example would look like:

record Foo(a: Int, b: Int)
val default = Foo(12, 9) // or: Foo(a = 12, b = 9)
val x = Foo(a = 10, ..default)
println(x) // Foo(10, 9)

I'm not sure I prefer this variant, but I thought to mention it since it looks a bit different than the other variants.

@marzipankaiser
Copy link
Contributor Author

marzipankaiser commented Aug 27, 2024

Many languages use keyword + default arguments, as you mention. Why do you think those are difficult to implement?

"Difficult" as in a bigger change to the codebase (they are of course simple to add in principle).

IIUC they would need to be changed in the parser and added in the source.Tree (+ handled in Namer, Typer) and translation to core (at least).

For the defaults there are multiple options on what is allowed and when this will be evaluated (from the top of my head: insert at call site, desugar to overloaded variant of the function, global constant).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request requires-design
Projects
None yet
Development

No branches or pull requests

3 participants