zenscript
zenscript copied to clipboard
Function Syntax
One of the first things we need to do is decide on a function syntax, for application and definition.
I don't think we want something too weird and new, so we should follow the conventions of existing language families. Do we want to follow the functional style which makes variable and function assignment similar (and maybe support partial application):
x = 1
f x = x
g x y = (x, y)
or do we want something from the 'C' family:
int x = 1
int f(int x) = x
(A, B) f<A,B>(A x, B y) = (x, y)
or something like Rust (without the types for inference):
let x = 1
fn f(x) { x }
fn g(x, y) { (x, y) }
Or perhaps python like:
x = 1
def f(x):
...
def g(x, y):
...
@keean wrote:
Do we want to follow the functional style which makes variable and function assignment similar (and maybe support partial application):
x = 1 f x = x g x y = (x, y)
I have argued here, here, here, and here to not to adopt that syntax.
Also that syntax is more ameniable to global type inference, because it otherwise requires a duplicate line to declare the type of the function.
Insurmountable negative factor is that vast majority of the world's programmers will be unfamiliar with it (unnatural, not second nature, fights their ingrained second nature), because it is too different from the most popular languages, JavaScript, Java, C, and C++.
I argued it raises the cognitive load too much both at declaration and at the call site, because it requires computing too many factors (inference, precedence, declaration site types and arity).
From the author of the acclaimed Learn You A Haskel:
I failed to learn Haskell approximately 2 times before finally grasping it because it all just seemed too weird to me and I didn't get it.
@keean wrote:
However I do think you have a point about people not used to the style.
You seem to have agreed?
Btw, partial application can be done with the other syntax styles. I will explain later.
@shelby3 I agree I just want to capture everything relevant here.
I also remember we wanted something like Python style indenting rather than curley brackets.
Do we want to have a keyword like 'def' or 'fn' for function definitions?
I actually quite like Rust's syntax for definitions, rather than Python or TypeScript. One problem is with combining TypeScript style signatures with Python style indenting:
inc(x : Int) : Int :
...
The problem here is we might want to leave out types:
dec(x) :
...
It makes it impossible to tell whether the return type is missing. Whereas with Rust style:
inc(x : Int) -> Int :
...
@keean wrote:
The problem here is we might want to leave out types:
I was just about to post a similar point. We can't use the C/Java-style of type precedes instance name:
int x = 1 int f(int x) = x
Also I wouldn't want some verbose gaudy type as the prefix any way.
If non-exported function signatures can be inferred, then a syntax with optional type annotations would seem better.
Agreed. Thus we need the suffixed
: typeand not the C/Java prefixed type declaration.
Haskell puts the optional types on a separate line instead which I think is unfamiliar, consumes extra lines and violates DNRY, which I don't like (then afaik all the types have to be provided, not the option of just some of them).
@keean wrote:
Do we want to have a keyword like
deforfnfor function definitions?
Since yesterday, I've been trying to think if there is a way we don't have to prefix the function declaration (aka definition) with fn or def (because I hate noise, unless it aids readability). I was going to work on the LL(k) parser grammar so we can prove it is a context-free grammar (CFG), which is very important. Also I want consistency with declaring methods in the body of typeclass definitions. So if we need to prefix with fn or def for local functions, then we should do same for methods, but I hate that boilerplate.
I've been thinking about all these issues too. :wink:
Inside the body of the typeclass declarations we certainly don't need the prefix on methods. But for functions declared inside the body of another function, we would have a grammar conflict with function calls. One way to get around that is to force a space between the function name and ( for declarations (e.g. f (x...)) and that whitespace not allowed for function calls/application (e.g. f(x...)). I may have other ideas. Let me think about it for a while.
The grammar is really a holistic design and we can't design it piecemeal and be sure to not have CFG conflicts.
Feedback?
@keean wrote:
I also remember we wanted something like Python style indenting rather than curley brackets.
inc(x : Int) : Int : ...The problem here is we might want to leave out types:
dec(x) : ...It makes it impossible to tell whether the return type is missing
I don't like the alternative syntax with an arrow inc(x : Int) -> Int :. It is inconsistent! Consistency is very important. And that trailing : is very confusing no matter what we do.
In the LL(k) grammar I was working on in May, I devised a solution which eliminates the need for the trailing : to indicate the start of an indented code block.
I presumed we will enforce a standard indent as agreed by @SimonMeskens.
The rule is that if the following line is indented by the standard amount, then it is a new code block. If it is indented more than the standard amount, then it is the continuation of the prior line.
I realize that enforcing indenting spacing is something that programmers will disagree on because they all have their preferential amount (and some want tabs and others want spaces), but this is open source and consistency (for readability) is more important than allowing people that freedom to make code unreadable for others (such as when the tabs don't line up!).
Btw, I am hoping we decide for 2 spaces indent to conserve horizontal space for deeply indented blocks? I notice from your examples, that you like more spaces when you indent (4 or 5?), but the really skilled programmers I've seen use 2 spaces, because 2 spaces is enough and also it helps align at the tail of the if on the line above:
if x
something
else
something else
Three spaces uses 50% more horizontal space and it aligns with the x which is confusing or consistent depending on the way one thinks about it:
if x
something
else
something else
Any block indenting greater than 3 spaces is going to piss off some programmers. Any programmers who think they need 4 spaces for block indenting are amateurish. Our eyes can't easily detect it at 3 spaces, and I find 2 is enough (and I am blind in one eye and blurred in the other eye).
What is your opinion? I could tolerate 3 but I don't think it is ideal. But 4 makes me not happy:
if x
something
else
something else
And 5 is ridiculous:
if x
something
else
something else
Most of my coding life I used 3 spaces, but when I saw that expert coders were using 2 spaces, I realized I didn't need 3.
What about the wildly popular arrow syntax? It's familiar, due to being included in JavaScript, TypeScript, Java and C# and it's quite concise (unlike using a keyword like def or fn). I'm using indents, but this would work with curlies or anything else too (I personally prefer required indentation). In Coffeescript, return is optional, but I really don't like that. What I do like is optional return for one liners. I kinda like the conciseness of not having to use brackets, but I agree that it makes the language rather dense to read and hard to get into. I agree with earlier sentiment that semi-colons are evil. You pay a penalty for all your code for that one time where you need them, usually to make your code less readable. I picked a thin arrow (->) instead of a fat arrow (=>), which is used more often, because I think the fat arrow is visually quite busy.
Block lamda (daft sample to pad it out a bit):
inc(x: Int): Int ->
let y = x + 1
return y
Notice that absence of type on y means the compiler implicitly assigns type, not that y is untyped.
Now, the problem arises for me, when you want to type curried functions or just simple lambdas. What do we use for indicating a type goes from one to the other? TypeScript for example uses the fat arrow for both, this is syntactically very confusing. I think for this reason, we could also replace the arrow with double colon. I personally think double colon is the nicest separator, but it's slightly less popular.
I'll introduce something else I like too: using := for assignment, like in Pascal. The reason being that this gels nicely with :: (both are a type of assignment, one assigns code, one assigns a value) and because it allows you to use = for comparison, as == for comparisons is visually busy and comparisons already tend to be the busiest syntax. I might even go as far as to suggest and and or for logical operators to further fix that.
filter(list: List of Item, pred: Item -> bool): List of Item ::
let out := List(Item)
for(item of list)
if(pred(item))
out.add(item)
return out
filter(myList, x => x = 5)
Notice that we might as well just drop the ::, as with forced indent, it's clear that the block indicates a function body.
inc(x: Int): Int
return x + 1
I kept the code quite imperative for now, to not assume too much yet. If we do get rid of brackets, I might prefer something like this I think? Still keeping brackets for calls maybe? Starting to get alien. Still less confusing than Haskell though.
filter
list: List of Item
pred: Item -> bool
return List of Item
=>
let out := List(Item)
for item of list
if pred(item) and not item.empty
out.add(item)
return out
filter(myList, x => x = 5)
@SimonMeskens wrote:
What about the wildly popular arrow syntax? It's familiar, due to being included in JavaScript, TypeScript, Java and C# and it's quite concise (unlike using a keyword like
deforfn).inc(x: Int): Int -> let y = x + 1 return y
That solves the : at end to start a new block, but I offered also an enforced indenting style as a solution instead, which removes that noisy arrow.
Your suggestion won't work for eliminating the def or fn as I explained where I offered inc ( with the space between inc and ( instead, because we need efficient LL(k) left-to-right parsing with minimal constant maximum number 'k' of tokens lookahead, but yours is an unknown variable unbounded number of tokens lookahead (for the parser to differentiate it from a function call, as parser has to reach a : to distinguish from function call, and : is optional on each argument for non-top-level functions).
I potentially like your idea of consistency between the syntax of anonymous lambda functions and regular function implementations (but maybe they shouldn't be equated since one is not a singleton, is not named, and other is a named, singleton?), but you've rendered the new block syntax inconsistent with if as follows (and I'm not sure which consistency should be our the priority choice?):
if x
something
Also this would be inconsistent with -> to start new block:
if x:
something
That seems fair enough, was just offering some ideas. Personally, I really like homoiconic languages, but I'm not sure how feasible that is for this one. Here's a good article on it:
https://blogs.oracle.com/blue/entry/homoiconic_languages
I am very sleepy so I hope the following is coherent.
On further thought, I must overrule my prior suggestion for an enforced number of spaces for indenting. As much as I would like to have this consistency, I don't think it will be popular because any choice will offend most people who don't like that choice. Number of spaces for indenting is a personal preference.
I looked at the grammar I wrote in May and now I remember how I solved this problem and maximized consistency.
First we must consider that these and if-else will also optionally be used without a code block and also that if-else will replace the ternary operator to simplify the language (more consistency) and because the if x: or if (x) at the start has lesser chance of precedence errors than "string" + x ?. Also I force a code block for nested if (as shown below).
So I suggest that if (x) is superior to if x: because the : when a new code block doesn't follow (i.e. what follows is on same line), then it could make be confused with a type annotation (or even a cast if we ever used that : Type format for casts). Also the : is more invisible when crowded.
if (x)
true
else
false
if x:
true
else
false
if (x) true else false
if x: true else false
if (x)
if (y) // may not put this at end of prior line, because it is a nested `if`
true
else
false
else
if (y) // may not put this at end of prior line, because it is a nested `if`
false
else
true
So if we choose if (x), then consistency with function definitions is lost in terms of the character that separates the code block, or code on same line. Thus I am comfortable with @SimonMeskens' idea except I favor the fat arrow (=>) because JavaScript and Scala use it and it looks more like an assignment symbol (=) so we can consistently use the same symbol for code block, one line function code (see below), and lambda (aka arrow) functions. Also afaik -> has typically been used for type signatures and not anonymous (unnamed) lambda functions, although Java 8 uses ->. The following definition now looks like assigning a lambda function to the function name, which is actually how JavaScript does it for { inc : (x) => return x + 1 }.
inc = (x: Int): Int =>
let y = x + 1
return y
inc = (x: Int): Int => return x + 1
Notice above instead of def, fn, or my hokey space idea, I used an = to differentiate from a function call inc(x...) needing only k = 2 tokens of lookahead (note inc no matter many characters is one token returned by the lexer to the parser). It is not shorter than def or fn, but it is more consistent with the usage of lambda functions, because we assign the unnamed lambda function to the function name.
Some people suggest to reverse the direction of the arrow?
That seems pretty clear to me :+1:
Some interesting ideas, I quite like the uniform assignment syntax. Haskell and other functional languages define functions as:
x = 1
f x = 1
It just means you need an LR parser. So I don't see a problem with:
x = 1
f(x) = x
I would suggest using '=' consistently. However I also like the anonymous function syntax and using normal assignment:
f = (x) =>
x
That looks odd for something like the identity statement.
I would suggest the block begins if there is nothing on the same line after the '=>' .
I think block indent depth should be set by the indent of the first line. Line continuations would be any kind indented more than this.
However there would be problems where there is a block and a line continuation like:
set_callback((x) =>
print(x)
x + 1
) // Must be at least as indented as the original, but less than the block indent.
What about optionally naming the anonymous function to help debugging. JS allows this:
set_callback(move_right(x) => x + 1)
I think these forms will require an LR parser, but I am okay with that.
Should we allow no brackets for a single argument function?
f = x => x + 1
@shelby3 wrote:
On further thought, I must overrule my prior suggestion for an enforced number of spaces for indenting. As much as I would like to have this consistency, I don't think it will be popular because any choice will offend most people who don't like that choice. Number of spaces for indenting is a personal preference.
But could we at least agree to disallow tabs for indentation (i.e. only spaces allowed)? My attitude is if it pisses off a few people, then I am unsympathetic to their preference, because if I as the reader don't know what tab width settings the programmer was using, then the blocks of code don't align on columns when I view it. This is an era of open source and code must be transferable without ambiguity.
Most programming text editors these days have an option to automatically convert all [Tab] keypresses to spaces.
Could I get a vote on the above suggestion? (the thumbs or thumbs down on this comment)
Another requested vote is should we disallow lines which contain only spaces? This would remind the programmer (and his IDE) to delete such lines from the code. Such lines make for clutter in version control differencing when they are later removed (cleaned up). Not putting them there in the first place would be better.
@keean wrote:
Should we allow no brackets for a single argument function?
f = x => x + 1
No. I am going to argue now at this early stage that we need to have some guiding principles. And I think one of those principles should be to not unnecessarily create multiple ways to do the same thing when we can easily avoid doing so (which is one of the complaints against Scala). Especially not creating another variant of syntax just to save two characters. This is inconsistency for the reader. And remember typically there can be 10 - 100 times more readers of each source code base, then there were coders of that source code. We want to minimize the number of variant cases the new programmer has to learn.
Okay, that makes sense to me, so it has to be:
f = (x) => x + 1
Presumably we are happy with all sorts of variations on type information like:
f = (x : Int) => x + 1
f = (x) : Int => x + 1
f = (x : Int) : Int => x + 1
I am not yet 100% firm on my prior suggestion to unify function definition around the =. I have another comment post coming about that and also responding to your prior comment about that, where you noticed some of the same issues I did.
@keean wrote:
Presumably we are happy with all sorts of variations on type information like:
The multiple ways to optionally annotate type declarations on arguments and result value, seems acceptable to me, because it is a consistent rule that also will apply to reference declarations as well.
On this tangent of variant syntax to define an anonymous (unnamed) function, I have liked a shorthand from Scala which enabled very concise expressions:
@keean wrote:
set_callback((x) => x + 1)
set_callback(_ + 1)
That is saving us much more than two characters and it can really remove a lot of verbosity if we further enable it to handle more than one argument.
I disliked that Scala used multiple _ to represent multiple arguments (or did Scala only allow one input argument for this shorthand? I forget), as these are undifferentiated:
set_callback2((x, y) => x + y)
set_callback2(_ + _)
I suggest instead with a limit of 3 arguments:
set_callback(_ + 1)
set_callback2(_ + __)
Note these should not cross function call lexical parenthetical scopes, i.e. the following are not equivalent:
setCallback((x) => f(() => x))
setCallback(f(_))
Normally I would argue against creating a new variant to do the same thing. But the above is so geek-cool, that I think it would be a beloved :heart_eyes: feature. My marketing senses/intuition prioritize love/passion :heart: over rigid design guidelines. Also I think I can make a case that the significant increase in brevity makes it more than just another way to do the same thing. We can't get that same brevity with the other syntax. Thus I argue it is necessary.
Am I mistaken on the love for this alternative syntax?
P.S. afair, Scala even allows the following, which I argue against, as it violates the guideline for not unnecessarily creating variant syntax:
set_callback(_:Int + 1)
set_callback2(_:Int + __)
I quite like the _ syntax, but there is probably quite a lot of other uses for it too. If we use it I would suggest we use it as a De Bruijn index, so that we have:
set_callback(_1 + _2)
@keean wrote:
set_callback(_1 + _2)
I had that idea also, but it makes it more verbose for the single argument case. If we prefer that, I'd perhaps suggest instead:
set_callback(_ + _2)
But I didn't suggest that because it is inconsistent, violating one of the other guidelines I have for not proliferating special case rules in order to keep the cognitive load for the new programmer reasonable.
Also in Scala that conflicts with the tuple member names. We might want to not reserve numbered names when they can be useful in general for names.
Also there is another reason I favored this:
set_callback(_ + __)
And that is because it visually literally looks like a slot (i.e. "fill in the blank"); whereas, _2 doesn't.
I do understand that _ and __ are somewhat similar in appearance and maybe not as readily differentiated to the eye, but on balance I think I prefer "fill in the blanks" than _2 which doesn't connote anything visually. And beyond 3 arguments is a diminishing benefit and we really need named arguments, because it gets too confusing to track the correspondence (I guarantee bugs :smirk:) even for ____ or _4 (and verbose compared to single letter argument names).
Another possible design choice, is only support it for one argument.
Anyone else want to share an opinion?
P.S. I know the choice is imperfect (I admit this _ + __ looks unbalanced and a bit weird) and I also consider not having the feature because it is not perfect, applying a guideline of not adding marginal syntax that can be done another way which isn't egregious. But I do love the brevity as being visually beautiful/elegant :gem: , which I think is another reason I prefer the connotation visually to slots. What do others think?
I find it hard to distinguish _ and __, and it gets even harder with ___ and ____. I think I prefer the Scala way where each slot is simply _ and you have to provide an argument for every slot. I think that is more readable and consistent. We can allow a 'let expression' if you need a slot more than once:
set_callback(let x = _ in x + x)
Although that makes me think how do we differentiate between a single like if and a multi line one? In Python function definition uses : at the end of the line so it would make sense for if. I think blocks (other than functions) might be a different topic, but it might not. It depends if we want consistency between functions and other statement blocks?
In the interest of brain dump disclosure...
I wish we could use Unicode symbols, then we could use (although it is a slippery slope towards "symbol soup" dingbats source code):
setCallback(⎵¹ + ⎵²)
Or:
setCallback(⑴ + ⑵)
Unfortunately Unicode doesn't define the character set for numbers above horizontal brackets, so the above are the two best available choices.
I realize we can't use Unicode for something a programmer needs to be able to type in quickly, but the world is changing and there are now configurable customized hardware keyboards and otherwise it is possible on Android, Windows, and Linux to software remap the keys on any hardware keyboard. A programmer can remember to type [Ctrl]+[1] keys simultaneously to generate a remapped ⎵¹ or ⑴. Since this is an optional shorthand feature that programmers don't have to use (and they can always resort to copy+paste if they haven't mapped their keyboard), I am not entirely joking. :laughing:
But I loathe special requirements on system configuration and/or specialized hardware. So this would need to be super compelling and it isn't.
It would also for the time being be a discrimination against the developing world, where they may not have access nor funds (or be forced to use a shared computer in a netcafe) to purchase the custom hardware keyboard.
Edit: Dec. 3, 2017 it’s possible to render with HTML+CSS the Unicode “bottom brackets” and “open box” with the superscripted number shifted in the desired position:

So I’m thinking the programmer could type _1 and IDEs could optionally render it as above. And __1 for the arguments of inline functions within a function call that is nested within an inline function already employing the _1 shorthand.
Special typing assistance isn’t required, i.e. the text can contain either _1 or ⎵¹. The editor (IDE) could optionally display them as Unicode as I above to make it more concise/pretty. That does cause an issue with inconsistent columnar alignment with different renderings, but I think that is an acceptable tradeoff. There’s no Unicode character which draws the superscripted number over the bottom brackets.
Ditto for example I will propose to allow ➜ instead of ->, but in this case the IDE could also optionally render -> as ➜ but many users might not choose to do so (as again it changes the displayed columnar alignment). I wish I could find a Unicode right arrow which is two characters wide.
@keean wrote:
I find it hard to distinguish
_and__
I don't (and I'm blind in one eye), but __ and ___ is borderline for me, especially if I am rushed or my second nature is on auto-pilot.
and it gets even harder with
___and____
Agreed. That is why I wrote we couldn't go beyond 3 arguments, maybe not even more than 2.
I think I prefer the Scala way where each slot is simply
_and you have to provide an argument for every slot.
But that isn't orthogonal to ordering, which sometimes can't be re-ordered to fit. And doesn't allow using the slot more than once (which you acknowledged).
And I find that confusing semantically (it is so inconsistent with the way variable names work). It adds cognitive load. I remember that being a source of confusion for me when I was learning Scala and still might be a gotcha when I am not paying careful attention.
We can allow a 'let expression' if you need a slot more than once:
set_callback(let x = _ in x + x)
That violates the generalized guiding principle that I thought we agreed on. It adds specialized syntax to fix the shorthand when the programmer can otherwise revert back to the normal syntax for unnamed function. And afaics that proposal gains nothing significantly justifiable in brevity:
set_callback((x,y) = y in x + x)
@shelby3 wrote:
That violates the generalized guiding principle that I thought we agreed on. You are proposing to add specialized syntax to fix the short-hand when the programmer can otherwise revert back to the normal syntax for unnamed function. And afaics you are proposing to gain nothing in brevity:
I thought having an assignment that you can use in an expression would be in line with the 'everything is an expression principal'. It would be usable in any expression, not just in the special short function syntax.
But that isn't orthogonal to ordering, which sometimes can't be re-ordered to fit. And doesn't allow using the slot more than once (which you acknowledged).
If you want to use each variable more than once or in a different order, use the original longer syntax :-)
I am not a huge fan of the _ syntax, but I can live with it, but the further we go down that route, the less I like it. I am tending to think we should stick to 'only one way to do thingsand say you just do(x) => x + x` its not really much longer and I think readability beats conciseness in this case.
@shelby3 wrote:
And beyond 3 arguments is a diminishing benefit and we really need named arguments, because it gets too confusing to track the correspondence (I guarantee bugs :smirk:) even for
____or_4(and verbose compared to single letter argument names).
and it gets even harder with
___and____Agreed. That is why I wrote we couldn't go beyond 3 arguments, maybe not even more than 2.
The proposed shorthand isn't suitable for more than 2 or 3 arguments, regardless of syntax we choose. It isn't justifiable to have _4, and the programmer should revert to the normal unified syntax for unnamed function definition in that case of so many arguments, because the brevity will be roughly the same due to the fact that single letters a, b, c, d are 4 characters shorter than _1, _2, _3, _4 not counting using a slot more than once.
Thus I logically conclude (including the logic in my prior comments) that the shorthand should only support at most 2 or 3 arguments and the syntax should be _ + __.
Otherwise choose not to have a shorthand syntax.
@shelby3 wrote:
P.S. I know the choice is imperfect (I admit this _ + __ looks unbalanced and a bit weird) and I also consider not having the feature because it is not perfect, applying a guideline of not adding marginal syntax that can be done another way which isn't egregious. But I do love the brevity ...
@keean wrote:
I thought having an assignment that you can use in an expression would be in line with the 'everything is an expression principal'. It would be usable in any expression, not just in the special short function syntax.
I failed to communicate my point. I am not making a decision here about whether an "assignment-as-expression" is desirable or not (since afaics that is mostly an orthogonal design issue, and since this wasn't a compelling use-case for it). Rather I am stating it doesn't make the use-case of the shorthand any less verbose.
But that isn't orthogonal to ordering, which sometimes can't be re-ordered to fit. And doesn't allow using the slot more than once (which you acknowledged).
If you want to use each variable more than once or in a different order, use the original longer syntax :-)
That logic is incongruent with my other point:
And I find that confusing semantically (it is so inconsistent with the way variable names work). It adds cognitive load. I remember that being a source of confusion for me when I was learning Scala and still might be a gotcha when I am not paying careful attention.
Point is that if we add the shorthand, then it should respect normal semantics of variable names. This is one of those insights into language design that Scala's designers apparently didn't do well in some (many or most?) cases.
I am not a huge fan of the
_syntax, but I can live with it, but I am tending to think we should stick to 'only one way to do things...
Didn't you write otherwise upthread? Or perhaps you meant all design uses for _ such as partial application?
I quite like the
_syntax, but there is probably quite a lot of other uses for it too.
Any way, holistic design requires a lot of thought, so it is easy to end up flipflopping.
I generally agree of course in the stated principle that "readability beats conciseness".
...and say you just do
(x) => x + xits not really much longer and I think readability beats conciseness in this case.
It isn't just the issue of being concise in string length, but also the crowdedness (symbol soupy-ness) of the juxtaposed double (( followed in close proximity by ) =>:
setCallback((x) => x + x)
setCallback(_ + _)
setCallback((x,y) => x + y)
setCallback(_ + __)
I have one more alternative syntax idea for the shorthand, assuming that we will set a rule that all variable names are lowercase (or at least that all variable and function names begin with a lowercase letter or underscore):
setCallback((x) => x + x)
setCallback(A + A)
setCallback((x,y) => x + y)
setCallback(A + B)
That would conflict with the convention of uppercase letters for type parameters (such as inside of typeclass method bodies), but we could require type parameters to be a minimum of two characters (or just disallow A, B, C, ... as many arguments we want for type parameters).
But I still think I prefer the underscore as it looks like a slot. Otherwise drop the shorthand proposal.
I love that we are putting all this design thought into the open source. :clap:
I think I may have a unified design solution to the shorthand goal. Let me think it out first.
I think structs/objects would probably start with an upper case letter. On reflection I think I would prefer to keep the 'one right way to do things' principle, and sacrifice the '_' for now. We can revisit later (maybe when we have used it a bit - self hosting the compiler should be the first project).