Closed ozra closed 9 years ago
This change would require changing the syntax of generic types. Otherwise this is ambiguous:
Foo(Int32)
Is that a new instance of the Foo
class? or is Foo
a generic class that's a generic instance type?
I'm not against this idea. In fact we though about it some time ago. But it will need a different syntax. Or we will need to change how to specify generic types, but Foo[...]
means array access and Foo{...}
means custom array or custom hash, and Foo<...>
is hard to parse (but could be an option, maybe by having Foo<
and Foo <
be different things).
There's also the thing that parentheses are optional in calls. So it would make sense to allow this:
Foo<Int32> 47
Point 42, 32
Which starts to feel strange. You can say "parentheses are optional except in implicit constructor syntax", but we don't like inconsistencies.
@waj, @asterite: Yes, I thought about that. One notation that came to mind was Foo[Bar] for generics, as you also mention. It gives a tighter impression in my eyes.
I'm not too fond of the C++ style (also used by Java, Kotlin, C# etc. C braces like linguas), even though I've coded it 16+ years. On a side note, there's a bunch of languages that require spacing all operators, but I'm uncertain that's a wise path, however still open to that idea.
The Foo[Bar]
shouldn't be ambiguous contextually and it should be possible to determine already at the parsing stage whether we're dealing with a type or an instance - if indeed the initial uppercase/lowercase rules are 100% consistent?
The optional parentheses would indeed be nice for this too in my eyes, and consistent which is good.
Some extreme examples with current and two proposed styles compared. This is kind of stupid to use as comparison though, since you'd most never write code like this: Note: Since the highlighter is not adapted to these different styles, I've purposely removed it to get a fairer comparison.
1:
foo1 = Foo(Int32).new 47
foo2 = Foo(Int32).new(47)
foo3 = Foo.new 47
foo4 = Foo.new(47)
point = Point.new 42, 32
def mystic_method(wants_type, wants_value); end
something = mystic_method Bar(Foo(Float64)), Bar.new(47.0)
nextthing = mystic_method(Bar(Foo(Float64)), Bar.new(47.0))
verboser = mystic_method(Bar(Foo(Float64)), Bar(Foo(Float64)).new(47.0))
2:
foo1 = Foo<Int32> 47
foo2 = Foo<Int32>(47)
foo3 = Foo 47
foo4 = Foo.new(47)
point = Point 42, 32
def mystic_method(wants_type, wants_value); end
something = mystic_method Bar<Foo<Float64>>, Bar 47.0
nextthing = mystic_method(Bar<Foo<Float64>>, Bar(47.0))
verboser = mystic_method(Bar<Foo<Float64>>, Bar<Foo<Float64>>(47.0))
3:
foo1 = Foo[Int32] 47
foo2 = Foo[Int32](47)
foo3 = Foo 47
foo4 = Foo.new(47)
point = Point 42, 32
def mystic_method(wants_type, wants_value); end
something = mystic_method Bar[Foo[Float64]], Bar 47.0
nextthing = mystic_method(Bar[Foo[Float64]], Bar(47.0))
verboser = mystic_method(Bar[Foo[Float64]], Bar[Foo[Float64]](47.0))
And, worth repeating, it should be kept in mind that the explicit type class instantiations likely will remain very uncommon.
Anyone have any crazy out of the box ideas for instantiation syntax? Wild ideas often distil down to innovative sane ones :-)
I'll close this. new
works well, it's just three chars, it's a method that you can redefine and give any meaning you want, etc. Another syntax will hide this and make users having to learn more things.
There's also the thing that parentheses are optional in calls. So it would make sense to allow this:
Foo<Int32> 47
Point 42, 32
Which starts to feel strange.
What's wrong with that? It's actually quite elegant.
Also, could :
work?
Foo:Int32(47)
I've been thinking about this for a while.
Used ~2488 times in the compiler source (some faux matches, semi-naïve RE)
Used ~5676 times in the compiler source:
I can't see why
new
doesn't have a terser()
"method"/operator mapped to it, while we are relieved of writingsome_array.get(some_index)
andsome_array.set(some_index, some_val)
- a construct used half as often. Both are too commonly used to necessitate cluttering them with latin lettered methods.Compared too:
Explicit generics are seldom used, thanks to inference, but would use doubled
()
:The first on creates the type instance from the generic type constructor (already established syntax), the second creates the value instance.
Reasons for
Types
being capitalized,vars
beginning with underscores (I think that's a great idea!) - has an ideal situation for visual disambiguation.Against
Thoughts? Wish from me: please try to see Crystal as a distinct unique language, even though inspired by a great clean syntax from another, there will definitely be more and more users of it from all kinds of language backgrounds, because it's great.