One thing that has been a little confusing to me is the .. vs ... operators for int ranges. I can never remember which is which: does more dots mean more numbers in the range? (no)
Last month I read through a Haskell tutorial where the range operators are .. and ..<. That is:
What I like most about these is that they translate naturally into speech: 1 to 5 vs. 1 to less than 5.
Maybe I've missed an old discussion where this was already debated, but I think it would do wonders for code clarity if we could reconsider the syntax in this case.
KevinKelleyTue 7 Apr 2009
I'm going with, two dots is the to operator; 1..5 is 1 to 5 is 1,2,3,4,5.
brianTue 7 Apr 2009
Those operators are taken straight from Ruby. I can't disagree that it takes a little while to get use to them. Here is something I found on the web a while ago:
Are you finding it hard to remember which is which? My way of remembering is: every range is the same width, in relation to the operators. Since the ... operator is wider, the end-point gets pushed outside the range:
v v
a..b
a...b
^ ^
I am open to changing to the syntax if something I really love is proposed, otherwise I'd prefer to copy an existing language. I don't particularly love either ..< or to.
tompalmerTue 7 Apr 2009
Groovy also uses .. and ..<, and I think they are much easier to remember. I think they started out with Ruby syntax but changed it along the way (presumably for the reasons brought up here).
andyTue 7 Apr 2009
Yeah I have the same problem remembering sometimes. I don't think its a huge deal - but I do sort of like the ..< syntax.
qualidafialTue 7 Apr 2009
I don't particularly love ..< either but it's more intuitive than ....
cbeustTue 7 Apr 2009
Any particular grammar problems with the math notation:
[1..3] = 1,2,3
[1..3[ = 1,2
]1..3[ = 2
?
brianTue 7 Apr 2009
Any particular grammar problems with the math notation
The index operator [] is orthogonal to range literals .., so the two syntax constructs don't know about each other.
For example:
fansh> r := 1..3
1..3
fansh> Int.random(0..100)
65
tacticsTue 7 Apr 2009
I like the ..< operator. It's simply more intuitive.
brianTue 7 Apr 2009
Here is the original discussion when we picked the two operators.
I also found this Groovy note (didn't search around for the mail list notes on the change).
Any more votes for changing from ... to ..< or other suggestions?
I don't have a strong opinion if everybody wants to switch.
qualidafialTue 7 Apr 2009
Out of curiosity, what does fan do with an exclusive range like 0...0? e.g. in fansh (0...0).toList raises an error.
In my testing in fansh I've also noticed that (3..0).contains(2) evaluates to false so I think there's a bug in there.
qualidafialTue 7 Apr 2009
Following up to my previous comment, I think it would make more sense for (0...0).toList (or (0..<0).toList) to return an empty list rather than throw an error.
brianTue 7 Apr 2009
In my testing in fansh I've also noticed that (3..0).contains(2) evaluates to false so I think there's a bug in there.
For whatever reason I made Range.toList support reverse ranges, but not contains and each. I pushed a fix so that all three toList, contains, and each handle both forward and reverse ranges consistently. I also fixed the empty list exception.
qualidafialTue 7 Apr 2009
I just finished writing some tests to capture this, I'll mail you the diff.
But in terms of Range.toList, each, and contains we do support reverse ranges:
fansh> (3..2).toList
[3, 2]
This seems like a practical trade-off to me. Slicing only supports forward ranges and negative indices have special semantics. On the other hand the Range methods are more pure mathematical. That inconsistency seems fine to me, but I'd be open to other suggestions.
qualidafialWed 8 Apr 2009
Regarding reverse ranges: in terms of slicing strings and list, if start < end then it is considered an empty range:
Did you mean to say if end < start?
brianWed 8 Apr 2009
Did you mean to say if end < start?
Yes sorry
qualidafialWed 8 Apr 2009
I started working on making this change in the compiler last night. I modified all pod sources (and fandocs) to use the new syntax. However I have to recompile the compiler pod to interpret the new operator before I can modify the compiler sources to actually use them.
I can run the buildboot.fan and buildpods.fan scripts successfully but when I try to copy the compiled pods from \dev\fan\lib\fan to \dev\rel\lib\fan and from \dev\fan\lib\java to \dev\rel\lib\java I get a compiler error:
The self-assignment checks are a recent addition on the official repository so I don't think this error is caused by my changes. Can somebody verify whether they are able to build from source and then build the source again against said build?
brianWed 8 Apr 2009
I have not modified the boot strap pods (build and compiler) to work with the latest changes. I am trying to keep everything working using the build 40 as the bootstrap compiler. Otherwise it is too easy to screw yourself with a compiler bug.
qualidafialWed 8 Apr 2009
How do you suggest I proceed? Should I revert all sources to use ... and then modify the compiler to allow both ... and ..< as a stopgap?
brianWed 8 Apr 2009
How do you suggest I proceed? Should I revert all sources to use ... and then modify the compiler to allow both ... and ..< as a stopgap?
In general I just change compiler and fix all the pods buildpods. Then when I am ready to upgrade the bootstrap is when I go back and fix compiler and build.
The first changeset (modifying the compiler to expect ..< instead of ...) is here
Modifying the compiler to actually use internally ..< is next.
I forgot to mention I'm getting a compiler error in sys/fan/Locale.fan that Locale.with should use the override modifier--this looks like overlap with the current work on it-blocks.
qualidafialWed 8 Apr 2009
I've updated my working copy to the official sources and following this process to try updating the bootstrap:
C:\dev\fan\src>fan buildboot.fan clean full
build completes successfully
C:\dev\fan\src>fan buildpods.fan clean full
ditto
Rename C:\fan\lib\fan\ to fan_orig
Copy C:\dev\fan\lib\fan folder into C:\fan\lib\fan
Rename C:\fan\lib\java\ to java_orig
Copy C:\dev\fan\lib\java folder into C:\fan\lib\java
Add override keyword to sys::Locale.with method to keep new compiler happy
This is run against the official fandev tip so this is definitely isolated from my changes--there is a problem in the way the compiler detects self-assignment.
brianWed 8 Apr 2009
I pushed the change to support "..<" to hg. I left the old "..." operator in place (I will take it out after next build). qualidafial I didn't use your repo b/c I had some merge issues - but pretty much the same changes.
This is run against the official fandev tip so this is definitely isolated from my changes--there is a problem in the way the compiler detects self-assignment.
Don't bother trying to update your bootstrap compiler - there are a couple things I have to fix for that.
qualidafialWed 8 Apr 2009
I think I found the reason my bootstrap build won't compile InitEnum: In Expr.fan the method LocalVarExpr.sameVarAs checks that two vars are using the same register but does not check whether they share the same context. So in InitEnum.addCtor the first local var (and thus the var in register #0) is:
MethodDef? m := null
Farther down the closure passed to ctors.each has a signature |MethodDef ctor|, so the ctor argument is in register #0 within the closure. So LocalVarExpr.sameVarAs returns true for the m and ctor expressions because they are both stored in register 0, even though they belong to different lexical scopes. Here is a test for testCompiler::CheckErrorsTest that captures this behavior:
Void testSelfAssignment() {
verifyErrors(
"class Foo
{
Obj a
new b(Obj a) { this.a = a } // ok
new b01(Obj a) { a = a } // self-assignment
new b02() { Obj a := 1; [2].each |Obj b| { a = b } } // ok
}",
[
5, 21, "Self assignment",
]);
}
brianWed 8 Apr 2009
I think I found the reason my bootstrap build won't compile InitEnum: In Expr.fan the method LocalVarExpr.sameVarAs checks that two vars are using the same register but does not check whether they share the same context.
Great catch Matthew! I started stubbing out the self assignment tests, but obviously they still needed some work. I pushed a fix which should fix the problem.
qualidafialWed 8 Apr 2009
I've pulled the update and it seems to have fixed the problem--only to surface another compiler bug:
qualidafial Tue 7 Apr 2009
One thing that has been a little confusing to me is the
..
vs...
operators for int ranges. I can never remember which is which: does more dots mean more numbers in the range? (no)Last month I read through a Haskell tutorial where the range operators are
..
and..<
. That is:What I like most about these is that they translate naturally into speech: 1 to 5 vs. 1 to less than 5.
Maybe I've missed an old discussion where this was already debated, but I think it would do wonders for code clarity if we could reconsider the syntax in this case.
KevinKelley Tue 7 Apr 2009
I'm going with, two dots is the
to
operator;1..5
is1 to 5
is 1,2,3,4,5.brian Tue 7 Apr 2009
Those operators are taken straight from Ruby. I can't disagree that it takes a little while to get use to them. Here is something I found on the web a while ago:
I am open to changing to the syntax if something I really love is proposed, otherwise I'd prefer to copy an existing language. I don't particularly love either
..<
orto
.tompalmer Tue 7 Apr 2009
Groovy also uses
..
and..<
, and I think they are much easier to remember. I think they started out with Ruby syntax but changed it along the way (presumably for the reasons brought up here).andy Tue 7 Apr 2009
Yeah I have the same problem remembering sometimes. I don't think its a huge deal - but I do sort of like the
..<
syntax.qualidafial Tue 7 Apr 2009
I don't particularly love
..<
either but it's more intuitive than...
.cbeust Tue 7 Apr 2009
Any particular grammar problems with the math notation:
[1..3] = 1,2,3
[1..3[ = 1,2
]1..3[ = 2
?
brian Tue 7 Apr 2009
The index operator
[]
is orthogonal to range literals..
, so the two syntax constructs don't know about each other.For example:
tactics Tue 7 Apr 2009
I like the ..< operator. It's simply more intuitive.
brian Tue 7 Apr 2009
Here is the original discussion when we picked the two operators.
I also found this Groovy note (didn't search around for the mail list notes on the change).
Any more votes for changing from
...
to..<
or other suggestions?I don't have a strong opinion if everybody wants to switch.
qualidafial Tue 7 Apr 2009
Out of curiosity, what does fan do with an exclusive range like
0...0
? e.g. in fansh(0...0).toList
raises an error.In my testing in fansh I've also noticed that
(3..0).contains(2)
evaluates tofalse
so I think there's a bug in there.qualidafial Tue 7 Apr 2009
Following up to my previous comment, I think it would make more sense for
(0...0).toList
(or(0..<0).toList
) to return an empty list rather than throw an error.brian Tue 7 Apr 2009
For whatever reason I made
Range.toList
support reverse ranges, but notcontains
andeach
. I pushed a fix so that all threetoList
,contains
, andeach
handle both forward and reverse ranges consistently. I also fixed the empty list exception.qualidafial Tue 7 Apr 2009
I just finished writing some tests to capture this, I'll mail you the diff.
alexlamsl Wed 8 Apr 2009
You can go Number Theorist and do:
jodastephen Wed 8 Apr 2009
The Groovy thread is interesting reading - I strongly recommend reading this one message, as well as the rest of the thread.
http://markmail.org/search/?q=groovy+range+operator#query:groovy%20range%20operator+page:2+mid:gz4ogz3hilxofqwe+state:results
In particular, a usability survey was done to determine the best style for the operators.
I would support
..<
as the exclusive range. I'd also question the negative range definitions based on the usability study in the link.andy Wed 8 Apr 2009
Alternativly we could just drop support for exclusive and simply have the single
..
operator? Do we really need both?Mabye, but in practice that is way too nice a thing to give up IMO.
brian Wed 8 Apr 2009
OK lets make the decision and switch from using the
...
token to..<
for exclusive ranges. I will do that for the next build.Regarding reverse ranges: in terms of slicing strings and list, if start < end then it is considered an empty range:
But in terms of
Range.toList
,each
, andcontains
we do support reverse ranges:This seems like a practical trade-off to me. Slicing only supports forward ranges and negative indices have special semantics. On the other hand the
Range
methods are more pure mathematical. That inconsistency seems fine to me, but I'd be open to other suggestions.qualidafial Wed 8 Apr 2009
Did you mean to say if end < start?
brian Wed 8 Apr 2009
Yes sorry
qualidafial Wed 8 Apr 2009
I started working on making this change in the compiler last night. I modified all pod sources (and fandocs) to use the new syntax. However I have to recompile the compiler pod to interpret the new operator before I can modify the compiler sources to actually use them.
I can run the buildboot.fan and buildpods.fan scripts successfully but when I try to copy the compiled pods from \dev\fan\lib\fan to \dev\rel\lib\fan and from \dev\fan\lib\java to \dev\rel\lib\java I get a compiler error:
The self-assignment checks are a recent addition on the official repository so I don't think this error is caused by my changes. Can somebody verify whether they are able to build from source and then build the source again against said build?
brian Wed 8 Apr 2009
I have not modified the boot strap pods (build and compiler) to work with the latest changes. I am trying to keep everything working using the build 40 as the bootstrap compiler. Otherwise it is too easy to screw yourself with a compiler bug.
qualidafial Wed 8 Apr 2009
How do you suggest I proceed? Should I revert all sources to use
...
and then modify the compiler to allow both...
and..<
as a stopgap?brian Wed 8 Apr 2009
In general I just change compiler and fix all the pods buildpods. Then when I am ready to upgrade the bootstrap is when I go back and fix compiler and build.
qualidafial Wed 8 Apr 2009
I've pushed my changes here.
The first changeset (modifying the compiler to expect
..<
instead of...
) is hereModifying the compiler to actually use internally
..<
is next.I forgot to mention I'm getting a compiler error in sys/fan/Locale.fan that Locale.with should use the override modifier--this looks like overlap with the current work on it-blocks.
qualidafial Wed 8 Apr 2009
I've updated my working copy to the official sources and following this process to try updating the bootstrap:
C:\dev\fan\src\compiler\fan\steps\InitEnum.fan(78,9): Self assignment
This is run against the official fandev tip so this is definitely isolated from my changes--there is a problem in the way the compiler detects self-assignment.
brian Wed 8 Apr 2009
I pushed the change to support "..<" to hg. I left the old "..." operator in place (I will take it out after next build). qualidafial I didn't use your repo b/c I had some merge issues - but pretty much the same changes.
Don't bother trying to update your bootstrap compiler - there are a couple things I have to fix for that.
qualidafial Wed 8 Apr 2009
I think I found the reason my bootstrap build won't compile InitEnum: In Expr.fan the method LocalVarExpr.sameVarAs checks that two vars are using the same register but does not check whether they share the same context. So in InitEnum.addCtor the first local var (and thus the var in register #0) is:
Farther down the closure passed to ctors.each has a signature
|MethodDef ctor|
, so thector
argument is in register #0 within the closure. So LocalVarExpr.sameVarAs returns true for them
andctor
expressions because they are both stored in register 0, even though they belong to different lexical scopes. Here is a test for testCompiler::CheckErrorsTest that captures this behavior:brian Wed 8 Apr 2009
Great catch Matthew! I started stubbing out the self assignment tests, but obviously they still needed some work. I pushed a fix which should fix the problem.
qualidafial Wed 8 Apr 2009
I've pulled the update and it seems to have fixed the problem--only to surface another compiler bug:
When I get some more time I'll try doing a bisect to find out which changeset broke it.
brian Wed 8 Apr 2009
That is just an
it
keyword conflict. I pushed a fix.