gleam-lang / gleam

⭐️ A friendly language for building type-safe, scalable systems!
https://gleam.run
Apache License 2.0
17.99k stars 750 forks source link

Rename record to map #31

Closed lpil closed 5 years ago

lpil commented 6 years ago

It may be worth doing this to avoid confusion with Erlang records

OvermindDL1 commented 6 years ago

Should still have a record type then though. The standard ML 'record' definition is often internally implemented near identically to how Erlang Records are (I.E. a tagged tuple), which has very nice performance benefits compared to Elixir structs (which are just maps internally). If anything a 'record' should be implemented as a tagged tuple and a row-typed record (known as an object in OCaml) should be implemented internally as a map, same as Elixir Structs.

I do not like the name of 'struct' for that though, not even in Elixir, as struct implies something hardcoded in size, which Elixir structs are not, unlike tagged tuples.

lpil commented 6 years ago

I'm not a big fan of the name struct for the same reasons.

I'd like to use similar language to Elixir and Erlang where possible, and my records/structs/whatever are implemented using maps so we can have polymorphic functions that accept any with a field of name X with value type Y.

An enum with a single variant is effectively a record, though it doesn't have the nice named field access.

I don't like the name object so much as I think people might confuse it for objects that have classes in an OOP setting.

OvermindDL1 commented 6 years ago

I don't like the name object so much as I think people might confuse it for objects that have classes in an OOP setting.

Ditto, a row-typed record 'is' still a record, just with named data instead of positional data, slightly slower but more flexible (like the record/struct distinction in elixir). But for OCaml it is what it is...

and my records/structs/whatever are implemented using maps so we can have polymorphic functions that accept any with a field of name X with value type Y.

So your records are already row-typed records? You really should support standard positional records as they do have a noticeable speed improvement in certain situations as well as are more reliably matched.

An enum with a single variant is effectively a record, though it doesn't have the nice named field access.

Except it also strongly types the data as well. This is a super common pattern in OCaml:

type stack_ptr = StackPtr of integer

type heap_ptr = HeapPtr of integer

And you don't have to worry about passing the wrong type to the wrong section, and yet it still internally compiles it to a raw integer, it effectively strongly types the wrapped type but still lowers it to that at compile time for efficiency reasons (OCaml is roughly C speed in execution, within a factor or so), it might even be a useful optimization on the beam.

lpil commented 6 years ago

a row-typed record 'is' still a record, just with named data instead of positional data

Agree, but I really want to avoid confusion with Erlang records. Besides the names listed above do we have any other options?

And you don't have to worry about passing the wrong type to the wrong section, and yet it still internally compiles it to a raw integer, it effectively strongly types the wrapped type but still lowers it to that at compile time for efficiency reasons (OCaml is roughly C speed in execution, within a factor or so), it might even be a useful optimization on the beam.

I think this optimisation is worth having, but I am planning to make it a different syntax than just an enum with a single variant, much like Haskell does with newtype. My reasoning is that I want the runtime representation of data structures to be very predictable so that Erlang/Elixir users can call Gleam functions easily. I don't think having a special case for a single constructor fits well with this goal.

OvermindDL1 commented 6 years ago

I don't think having a special case for a single constructor fits well with this goal.

I agree, OCaml only did it that way because it tries to minimize syntactical overhead, but sometimes adding in more is great for clarity. :-)

OvermindDL1 commented 5 years ago

Just a passing idea, an unpacked strongly typed head:

type blah = Blah of int

Could compile down to just an integer, where:

type blah =
| Blah of int

(* or, same declaration *)

type blah = | Blah of int

Compile down to (in Elixir format) {:Blah, 42} or so.

Easy disambiguation then.

lpil commented 5 years ago

I dig it! I think this is where we will go :)