Page 1 of 5

Require spaces around binary ops

PostPosted: Wed Jul 03, 2013 12:45 pm
by Charles
Actually, spaces are already required around named binary ops such as "and" and "or". But we also have these binary ops:

arithmetic: + - * / ...
assignment: = += -= ...
bitwise: & | ...
coalesce: ?

I mentioned on another thread:

Sometimes I think programming languages would be better off requiring a space around binary operators such as comparisons, arithmetic, etc.


For which there were two positive responses. Although one was based on the idea of having dashed identifiers and I can't honestly say I was planning that. What I am planning if we introduced this constraint is two new convenience operators:

# Spread operator
items*.clear
# equivalent to:
for item in items, item.clear
# another example
names = customers*.name
# -->
names = for c in customers get c.name


# Safe navigation operator - avoid null ref exceptions
companyName = user?.company?.name

Both of those operators are not currently possible in Cobra because they are ambiguous with multiplication and coalesce, respectively.

Another advantage to requiring spaces around binary ops is a more uniform coding style between code bases. Also, a more consistent coding style within a given code base. Also I wouldn't have to edit patches as much. :-) I often get:
a= 1
b =2
c = 3
...in the same patch.

Playing devil's advocate, disadvantages include:

-- Impact on legacy code.
-- New users of Cobra may be turned off by what they perceive as pickiness.
-- The above new operators may still look ambiguous (although I'm skeptical of this point).

Your thoughts?

Re: Require spaces around binary ops

PostPosted: Wed Jul 03, 2013 12:48 pm
by Charles
Oh, and the dot operator as in "foo.bar" would obviously be excluded. :D

Re: Require spaces around binary ops

PostPosted: Wed Jul 03, 2013 1:32 pm
by nerdzero
I think spaces around all binary operators is a great idea.

I like the spread and safe navigation operator ideas as well, but I worry it would generate a lot of "write-only" code.
names = users*.company?.name

For me, that's hard to visually parse and it takes a second or two for my brain to figure out the type that 'names' gets. I suppose I could get used to reading it though once I saw it enough.

Would this be a valid use of the spread operator or would it only be for member access?
require all numbers* > 0

Re: Require spaces around binary ops

PostPosted: Wed Jul 03, 2013 2:10 pm
by Charles
I'd like to think that over time you'd get use to it. But we can add that to the list of potential disadvantages:

-- May make the code harder to read.

Re: your question with comparions, if we require the "all" or "any" keyword, then it's not clear if the trailing "*" is even needed. In other words, we could potentially support "all numbers > 0" right now. Well actually lets think about member access. Like:
trace any customer*.balance > 0

So actually, the trailing * makes more sense if we want to use this in more circumstances.

While we're on the subject, I'm also thinking about "accumulators" or "aggregates". Something like:
sum = 0
for item in items, sum += 0
trace sum
# how to get to one line and preferably even an expression?
# maybe:
trace 0 + items*
# general form:
trace <init> <operator> <spread collection>

That's just speculation right now. I haven't thought about all the implications. Comments are welcome.

Re: Require spaces around binary ops

PostPosted: Thu Jul 04, 2013 1:19 am
by hopscc
When you posted that note I thought it might be advantageous from a standardised code presenttaion viewpoint
(like no leading mixed spaces and tabs and warning about unneeded () and various others ),

The downside is it makes cobra compiler a lot more pickier ( or more whinier) about code it accepts esp when its nitpicking about
(cosmetic) separation whitespace which is really bad when you're just trying to get something working really fast..
( provide switch for (slightly) relaxed mode on compiler ??)

The pickiness/whining is bad enough now (tho for good reasons).
I could probably go for forcing this on assign (=) as a tester...

With this, theres goanna be 2 classes of Ops then - ones that must have surrounding spaces ( and, or, not to to? , ... math +-*/, assign = +=, -+, *= /=, BitWise,
(non)Coalesce - ?,!
and ones that cant ( or shouldnt) - DOT . , QDOT ?. , STARDOT *.

(The need to correct ws around assigns (and other ops) in patches probably means that noone sees wspace here as of importance - or that we need a pretty-printer)

Re new Ops:
the safe-null operator (?.) I can see a (small)need for
(called the elvis operator ( in groovy/Ruby its a null-coalesce (?:), was proposed for Java (and maybe in others) exactly as here)
Small need cos nil tracking removes some/much of the issue .

I'm less certain about the 'spread' operator - its a new op for a (reasonably) understandable and explicit readable code - Notational shorthand is fine but it seems more obscurative than clarifying..

I dont think that construct is used enough to need its own special op ( or that making it an op would be a boost to it being used more)...
- Doesn't python have something similar (wrt comprehensions(??) which are suggested ( google python coding stds )to be only used for very simple cases - if slightly more complex clearer to expand them out fully.

I'm not sure that you assertion of being unable to support ?. and *. without space forcing is accurate.
2 new tokens '?.' and '*.' recognised only between naming ID paths and broken out to separate pieces...
I might try it for the elvis operator...

More comments on rest later...

Re: Require spaces around binary ops

PostPosted: Thu Jul 04, 2013 2:58 am
by hopscc
With the aggregator dont you also need an operators 'step' expression?

trace <init-expr> <op> <step-expression> <spreadCollection>
#for
sum = <init-expr>
for item in items, sum <op> <step-expression>
trace sum

result = 0 + 1 items*

If its just to be limited to number accumulation/summing then dont need <op> just a <step> value

Your example and above as space separated items look too much like malformed/wrong 'normal' expressions, the construct doesn't 'read' like any other expression though they look exactly like them - ( so theres a huh? moment)
the '0 +', '0 + 1' doesnt give any indication they are each separate pieces rather than some strangely placed expression ( '0 +'/'0 + 1')

How about some joining separators (like slices and counted-for) which can then elide to obvious values (also like those)
result =  0 : + : 1  items*  # spread last
result = items* 0 : + : 1 # spread first
#elided init=0, accumulatorValue =1
result = items* : + : # spread first
result = : + : items* # spread last
# Opinion: the embedded lone op just looks wrong

# If limit to simple string/numeric accumulation only then same as
#sum=<init>, for item in items, sum = sum + <step-value>
result = 0:1:items*
result = '':'-': strItems* # string of - to nItems in items

#perhaps better/more general : just two expressions - first named id in init-expr is value returned fm expression
<spread> <init-expr> : <step-expr>
result = items*: sum=0: sum+1 # spread first
result = sum=0: sum+1: items* # spread last

# More explicitly/redundantly perhaps
#<variable>=<init-expr>, for item in items, <variable> = <step-expr>
# <spread> : <result-variable> : <init-expr> : <step-expr>
result = items* : sum : 0 : sum+1

Re: Require spaces around binary ops

PostPosted: Thu Jul 04, 2013 3:07 am
by hopscc
Is there any negative collision ( syntactically or visually/conceptually) with Spread and similar syntax for a Stream (, platform-independent Type for an enumerable/iterable collection)
x as String*  = List<of String>(...) 
...
tx = x*.trim



The Spread syntax is shorthand for code walking a Stream (??)

Re: Require spaces around binary ops

PostPosted: Thu Jul 04, 2013 4:40 am
by hopscc
Assuming someone had the parsing of nil-safe working and everything working through to codegen :)
What do we all think would be the simplest way to implement a nil safe check in c# code for a platform ( like c#) that didnt have that capability/operator available or built in ?
( preferably with least code perturbation i.e without writing/rewriting the parts of the name dot id chain multiple times )

Re: Require spaces around binary ops

PostPosted: Thu Jul 04, 2013 11:56 am
by Charles
hopscc wrote:I could probably go for forcing this on assign (=) as a tester...

Sounds like an idea.

hopscc wrote:With this, theres goanna be 2 classes of Ops then - ones that must have surrounding spaces ( and, or, not to to? , ... math +-*/, assign = +=, -+, *= /=, BitWise,
(non)Coalesce - ?,!
and ones that cant ( or shouldnt) - DOT . , QDOT ?. , STARDOT *.

Right, but that's kind of the point. Both ?. and *. need to be put together to disambiguate at a syntactic level.

hopscc wrote:(The need to correct ws around assigns (and other ops) in patches probably means that noone sees wspace here as of importance - or that we need a pretty-printer)

Well at least the people submitting the patches don't. ;)

hopscc wrote:I'm less certain about the 'spread' operator - its a new op for a (reasonably) understandable and explicit readable code - Notational shorthand is fine but it seems more obscurative than clarifying..

I dont think that construct is used enough to need its own special op ( or that making it an op would be a boost to it being used more)...

Actually I find that I would use it more than the safe-null operator. This comes up all the time:
for foo in _foos, _foo.doSomething
# -->
_foos*.doSomething

Throw in "all foo*", "any foo*" and aggregation, and I think it will find plenty of use.

hopscc wrote: - Doesn't python have something similar (wrt comprehensions(??) which are suggested ( google python coding stds )to be only used for very simple cases - if slightly more complex clearer to expand them out fully.

Python's list comprehensions are most similar to Cobra's for-expression. I don't think Python has anything like the spread operator.

hopscc wrote:I'm not sure that you assertion of being unable to support ?. and *. without space forcing is accurate.
2 new tokens '?.' and '*.' recognised only between naming ID paths and broken out to separate pieces...
I might try it for the elvis operator...

foo*.bar
# currently means local var 'foo' multiplied with member access '.bar'
foo?.bar
# current means local var 'foo' nil coalesced with member access '.bar'
# although I think with those semantics, both expressions read better with spaces:
foo * .bar
foo ? .bar

Re: Require spaces around binary ops

PostPosted: Thu Jul 04, 2013 12:05 pm
by Charles
hopscc wrote:With the aggregator dont you also need an operators 'step' expression?

trace <init-expr> <op> <step-expression> <spreadCollection>
#for
sum = <init-expr>
for item in items, sum <op> <step-expression>
trace sum

result = 0 + 1 items*

If its just to be limited to number accumulation/summing then dont need <op> just a <step> value

You lost me on why a step is needed when summing items.

My idea was that given a sequence of items, I want to add them all or multiply them all. If you mean to provide a step so that items can be skipped, my two thoughts are:

1) This hardly comes up in practice.
2) You can slice out the steps and then apply the operator to that

Anyway I haven't thought enough about applying math ops to streams. Can revisit later.