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

Struct field access syntax #240

Closed
lpil opened this issue Sep 16, 2019 · 3 comments
Closed

Struct field access syntax #240

lpil opened this issue Sep 16, 2019 · 3 comments
Labels
help wanted Contributions encouraged

Comments

@lpil
Copy link
Member

lpil commented Sep 16, 2019

struct Thing {
  doodad: Int
}

fn go(x: Thing) {
  x.doodad
}
@lpil lpil added area:compiler help wanted Contributions encouraged labels Sep 16, 2019
@lpil lpil closed this as completed Sep 22, 2019
@OvermindDL1
Copy link

Should probably also support Thing.doodad being a function that takes a Thing and returns the doodad field from it.

If you want to support row-typing as well as nominal typing then you could also go the route a lot of ML languages have done and make a generic .doodad function that returns a field of that name from any struct/record that has a field of that name (or fails to compile).

@lpil
Copy link
Member Author

lpil commented Sep 23, 2019

Yes totally. I've closed this issue for now as I want to put more thought into the best way to do this.

You mentioned first class lenses before, I would be interested in seeing any material on your ideas if you have any knocking about :)

@OvermindDL1
Copy link

OvermindDL1 commented Sep 23, 2019

Full on first-class lenses would be a lot more powerful!

In essense for the OP example, you could have .doodad make a lens, a Lens struct would just be a struct with two fields, a get and an update field (although some systems add in more for efficiency reasons, those are the base two that you need though and is still what most systems do), the fields of which just hold functions. So for something like .doodad on the OP Thing struct, it would create a Lens struct like (in Rust'ish notation as I haven't learned gleam's notation yet ^.^;):

// Global `Lens` definition:
struct Lens<Object, Result> {
  type Result;
  get: Fn(&Object) -> Result;
  update: Fn(Object) -> Object
}

// Create the `.doodad` lens
Lens<Thing, i32>{
  get: |&thing| -> thing.doodad
  update: |doodad, thing| -> Thing{doodad: doodad, ..thing}
}

Which then given a get function and an update function (and you can make others from those two like an update_in that takes a function to transform or so forth as well), you could use it like get .doodad some_thing or update .doodad 42 some_thing, traditionally the value preceeds the object for ease of piping reasons in curried languages, but whatever is appropriate for the language. And of course you can chain them together, like if you had this:

struct Thing {
  doodad: Int
}

struct HoldsAThing {
  thing: Thing
}

Then you could compose them either explicitly like compose .thing .doodad (or to disambiguate if identical names are in scope you can use full names like compose HoldsAThing.thing Thing.doodad), and if you assigned that to a binding named something like thing_in_holder then you could still use it like get thing_in_holder holder or update thing_in_holder holder 42 or so.

A common pattern in some languages is that a tuple of lenses acts as an implicit compose as well, so (.thing, .doodad) is the same as compose .thing .doodad when passed to the get/update functions if it is a language that supports different function types on identical function names (which since the OTP doesn't support currying efficiently then might be a good feature to add to gleam).

And of course, since a lens is just a struct/record, can call it's callbacks straight too .doodad.get thing or .doodad.update 42 thing.

And of course, you can have a pretty trivial pass in the compiler so that if something like get .doodad thing is used then you can optimize the creation of the two closures to just become a direct call too pretty easily.

For note, haskall does implicit composition of lenses by doing things like get (thing . doodad) holder; it doesn't use .thing syntax instead of thing because the fields of structs become 'magical' once compiled, like loose functions, it's generally not a recommended language pattern nowadays and the .thing syntax has become pretty standard now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Contributions encouraged
Projects
None yet
Development

No branches or pull requests

2 participants