Open stugol opened 8 years ago
Could even support a terser syntax, that compiles to the same thing:
get-fred ->
rec Person
name = "Fred"
number = 1
The types of name
and number
can be inferred if not specified, and the same code generated as if the values were specified outside the record definition:
get-fred ->
rec Person
name
number
Person.new "Fred", 1
No error should result in the compound case:
get-fred ->
rec Person
name = "Fred"
number = 1
Person.new "Fred", 1
If all the values are specified, as above, rec
should return an instance. In either case, the values should be considered defaults and can be overwritten:
get-fred ->
rec Person
name = "Fred"
number
Person(number: 1)
get-two-people ->
value1 = rec Person
name = "Fred"
number = 1
[value1, Person("John", 2)]
If any values remain unspecified, rec
should return void
.
value1 = rec Person -- error!
name = "Fred"
number
Before you ask: The real-world application for this is simply that returning an anonymous tuple is bad design:
my-fn ->
{"Fred", 1, true, 27, #green} -- WTF!? What do these signify exactly?
It's data without context. This is bad. It's the same reason we name constants in C++.
I totally agree it should be possible, not only to define types, but also functions, withing functions. I've been thinking about that since I started, glad you reminded me!
Let's break this down a bit:
With regard to the rec
(which in that case should definitely be called record, which then of course clashes with the stdlib-macro from crystal... but), the path chosen atm for type is a bit "opposite" to that, for example: enum Something
in Onyx (currently!) is type Something < enum
, that is, all type definition follows ("type" TYPE_NAME "<" TYPE_BUILDER BASE_TYPE?
[simplified]).
A record out of that perspective would be type MyRecord < record
- which no doubt looks a bit ugly and off-beat for such an "inline code"-construct.
I think the best solution here might be to use the crystal stdlib macro, but change the behaviour so that types can be defined within functions (we all want that).
I also think (technical detail) the temp-name should derive more data from code - making debugging easier:
type __anon_type__get_fred__Person
name Str 'get
number Int 'get
init(@name, @number) ->
get-fred() ->
__anon_type__get_fred__Person "Fred", 1
Notes for above:
new
in Onyx. "Calling" a type is obviously enough creating an instance of it - but you can of course call new if you want to)'get
and 'set
- this keeps inst-var names spatially aligned to the left.@
before names of inst-var definition (reflecting their use-syntax)I agree that having values' meaning specified is better, however with return values like in the example it's harder in a way: You can't easily specify that type outside of the function, if you'd need to (well, typeof() could be used). A future tuple with named elements could come in handy there - but this is just a "side-rant", not really important.
my-fn() ->
<name: "Fred", number: 1, wtf: true, age: 27, fave-colour: #green>
On the suggestion:
value1 = rec Person
name = "Fred"
number = 1
get-two-people() ->
record Person
name = "Fred"
number = 1
[Person(), Person("John", 2)]
Another note: untyped inst-vars is not allowed. You've read the discussion regarding this in crystal issues I presume. This is not a major problem since simply assigning defaults like in your example infers it. I'm eager to refactor onyx-compiler into a daemon when the language has settled. It can then re-compile only changed parts and you could basically have a fully compiled program at each save in your editor with minimal CPU-cost. That will rock. (daemon-mode will be optional!)
With regard to some of the ideas discussed above: macros are used. There is one major thing still lacking in Onyx, and that is the macro/template handling:
So anything involving macros should be treated as twilight zone atm. I'm confident I'll solve this, and I'm currently coding on the parts required to get this working seamlessly - actual syntax for Onyx coming in last - it should sit well. (This is why nothing's "happening" on issues atm - this takes some effort)
the path chosen atm for type is a bit "opposite" to that
I don't follow.
I also think the temp-name should derive more data from code - making debugging easier
Of course. My naming was merely an example.
it's recommended to not use new in Onyx
Noted.
The recommended way of creating getters and setters in Onyx are via the inst-var pragmas 'get and 'set - this keeps inst-var names spatially aligned to the left.
I see. So if we want the object to be mutable, we simply add 'set
to the variables?
I'm still considering requring @ before names of inst-var definition
You should. Except in a record
, where you're conceptually defining a structure, not a class.
with return values like in the example it's harder in a way: You can't easily specify that type outside of the function, if you'd need to
Why not simply make a record
be a labelled tuple? I define it with rec
because I want to explicitly specify the types of its members - and their mutability - but the eventual object is a subclass of "labelled tuple".
If it's a one-off instance, then anon-type-style should be used instead.
You mean <~
?
Another note: untyped inst-vars is not allowed. You've read the discussion regarding this in crystal issues I presume.
I have not, but it makes sense.
I'm eager to refactor onyx-compiler into a daemon when the language has settled.
Sounds useful.
type
only, and the type builder
modifier for explicit diversions (like enum
: type MyEnum < enum
, which is like the "opposite" of going with a record
keyword. But it's use-case is different enough that a diversion would be in order if it would be seen fit.@
-prefix: As long as it's still just a macro they're not needed, and if a keyword would be deemed better for some reason, it's already diverging from the type syntax, so that would be a good idea then.
Crystal doesn't allow records to be declared inside a function:
This is because
record
is a macro that generates a class, and you can't have classes inside functions. However, strikes me that your #49 visitor pattern lets us create anonymous classes wherever we want, right?You also can't specify types for record members in Crystal. I propose we solve these two problems at a stroke:
Of course, we'll want to simplify it a bit. Such ad-hoc types are essentially records, and all their fields should be public. So we can have a terse syntax such as:
This will presumably compile to an anonymous class at file-scope:
Propose also
mutable rec Person
, generating a class where all the fields are read-write (attr_accessor
instead ofattr_reader
). Mutability should be discouraged unless required, so making the default immutable makes sense.