Something we might want to keep track of is a list of Fan "Gotchas" for people coming from other languages. If you come up with any more, list them here:
Int[] list := Int[1024] creates a list with a single element: the integer 1024. To do Java's equivalent of new int[1024] in Fan, use the List.fill method.
Use := for initializing a variable, not =.
The empty list literal is [,] not [] and the empty map is [:] not [] or {}.
(Correct me if I'm wrong on this) When using the comma notation in an it-block, such as MyThing { obj1, obj2, obj3 }, the compiler constructs this as MyThing.make.add(obj1).add(obj2).add(obj3). You need to make sure that MyThing.add has a This return type!
The default construct is called make and does not share the name of the class. It is marked with the new keyword in place of a return type.
The compiler automatically inserts downcasts when it can't see the cast is impossible. For example, the code Obj x := 42; Str y = x; compiles but throws a runtime error.
Characters are represented in Fan as Ints corresponding to the character's Unicode code points. Be careful when appending characters to the end of a string! The code "Lol" + s'' results in the string "Lol115", not "Lols" like you might expect. Use the toChar method to convert Ints to Strs.
...since List.remove(Obj) is removing an Int that may or may not still be in the list; removeAt(index) is what I wanted.
Most of the time you'll get a compile error there, but not when the index and the content are of the same type.
tompalmerWed 6 May 2009
When using the comma notation in an it-block, such as MyThing { obj1, obj2, obj3 }, the compiler constructs this as MyThing.make.add(obj1).add(obj2).add(obj3). You need to make sure that MyThing.add has a This return type!
Just tried it, and that's what I get, too. I personally think this should be changed. It should do repeated calls to add on the original object, in my opinion.
tompalmerWed 6 May 2009
Or perhaps some kind of addAll, if it exists?
qualidafialWed 6 May 2009
It should do repeated calls to add on the original object, in my opinion.
Wouldn't this break on a const class? i.e. MyImmutable.add may return a new MyImmutable that is identical save for the additional collection element.
brianWed 6 May 2009
Just tried it, and that's what I get, too. I personally think this should be changed. It should do repeated calls to add on the original object, in my opinion.
Technically a list of expressions separated by a comma is a single expression itself which is why it makes sense to chain the adds together. That and it is more efficient. I debated this, and decided this is the right way (despite being a gotcha). BTW this is how normal list literals work too:
[0, 1, 2] => List.make(Int#).add(0).add(1).add(2)
Or perhaps some kind of addAll, if it exists?
That would be a more expensive since you'd have to build a list first (just to destructure it again).
Wouldn't this break on a const class?
That is another reason I decided to make it a single chained expression. Although write now the result can't be assigned to anything, so we still can't really use it for immutables.
tacticsWed 6 May 2009
Technically a list of expressions separated by a comma is a single expression itself which is why it makes sense to chain the adds together.
Additionally, it supports the case where a class has an "add()" function which returns a modified copy instead of using side effects. (Or at least, I think that's possible).
Gotchas are fine in a language as long as they don't do the opposite of what you expect.
Though in this case, if add() IS used in a comma expression, it might make sense to throw a more informative error. Right now, the error generated is triggered by the code after de-sugaring, but it doesn't seem too hard to do a check before de-sugaring.
qualidafialWed 6 May 2009
Does the compiler generate an error if an add expression in an it-block is applied to a type where the add method returns something other than This?
brianWed 6 May 2009
Though in this case, if add() IS used in a comma expression, it might make sense to throw a more informative error.
That is a good idea, I will add a meaningful error message.
tacticsWed 6 May 2009
Does the compiler generate an error if an add expression in an it-block is applied to a type where the add method returns something other than This?
It throws whatever errors the expansion would be. For example, if MyThing.add is defined as a Void-valued function, then with the code:
MyThing
{
obj1,
obj2,
obj3
}
The resulting expansion inside the compiler would be
MyThing.make.add(obj1).add(obj2).add(obj3)
And the result is an error message like "Cannot call .add on a Void-valued expression.
alexlamslThu 7 May 2009
Although {right} now the result can't be assigned to anything, so we still can't really use it for immutables.
Would you mind elaborating on that point? I would expect MyThing{obj1,obj2,obj3} to work even with:
const class MyThing {
const Obj[] elements
This add(Obj e) {
Obj[] list = new Obj[,]
list.addAll(elements)
list.add(e)
return MyThing{elements = list.toImmutable}
}
}
So with is the one returning the value. I suppose it could return the return value of the it-block, which would make the example work, but then what to do for most it-blocks that have no need to return any value???
alexlamslThu 7 May 2009
Ah I see - the sugar hypes me out, eventually >____<"
In this case, I guess with could make a judgement between |This->Void| and |This->This|, with any other cases being illegal it-blocks.
brianFri 8 May 2009
Though in this case, if add() IS used in a comma expression, it might make sense to throw a more informative error.
I added a better error message.
qualidafialFri 8 May 2009
Should it-blocks return This if they use the comma syntax to add items? This aligns with the requirement that the add method returns This, and would enable alexlamsl's const usage above, thus:
Of course sys::Obj.with would then have to distinguish between it-blocks that return Void (and in those cases return this) and it-blocks that return This (and in those cases return the result from the block). Not sure if this would be possible.
brianFri 8 May 2009
Should it-blocks return This if they use the comma syntax to add items? This aligns with the requirement that the add method returns This, and would enable alexlamsl's const usage above, thus:
I don't think we can really do that without forcing everyone to return a value from a with-it-block (which would be annoying).
But you can solve the problem with immutable classes using a builder:
const class Foo
{
new make(|Obj[]| f)
{
builder := Obj[,]
f(builder)
list = builder
}
static Void main()
{
x := Foo { 0, 1, 2, 3 }
echo(x.list)
}
const Obj[] list
}
alexlamslFri 8 May 2009
I don't think we can really do that without forcing everyone to return a value from a with-it-block (which would be annoying).
It would be annoying only if you have to type out return it explicitly.
Perhaps we can have return it as an implicit statement unless a return statement exists within the it-block?
JohnDGFri 8 May 2009
Perhaps we can have return it as an implicit statement unless a return statement exists within the it-block?
Self-return is a special and tedious case. It would be interesting to toy with the idea of auto returning it/this depending on the return type. Basically what Void does now.
yachrisSat 8 Aug 2009
Here's a new gotcha (or maybe I'm an idiot :-)
I was looking at:
#! /usr/bin/env fan
using fwt
class FwtHello : Test
{
Void main()
{
Window { Label { text = "Hello world" }, }.open
}
}
and refactoring towards more functionality (I wanted to bind a Button to a method, but that's irrelevant to this discussion) so I wanted to move the Window creation out to a method. So I did this:
#! /usr/bin/env fan
using fwt
class FwtHello : Test
{
Void main()
{
fh := FwtHello()
}
new make()
{
Window { Label { text = "Hello world" }, }.open
}
}
Which seems reasonable, until you run it. It runs fine, but if you click the window's close box, the window comes back immediately! The second close of the window makes it go away for real. And if I use Command-Q (the mac's key-binding to quit a program) I get a lovely backtrace. For the record, I first saw the double-window-show at work on Windows, so it's not a Mac thing.
Finally, I did the following:
#! /usr/bin/env fan
using fwt
class FwtHello : Test
{
Void main()
{
fh := FwtHello()
fh.open()
}
new make()
{
m_window = Window { Label { text = "Hello world" }, }
}
Void open()
{
m_window.open()
}
Window m_window
}
which (A) works and (B) makes sense, but... I'm confused. Why would the second one do the double-window thing? And should this be explained somewhere?
Thanks!
brianSat 8 Aug 2009
I haven't tried to run your code, but I expect it is because your main is instance based and not static, so when you run main what you are really doing is this:
FwtHello().main // create instance of FwtHello
Try changing your main to static
yachrisSat 8 Aug 2009
Try changing your main to static
Bingo! That fixed it. Thanks.
Raises the interesting question... should a non-static main function the same as a static main?
tacticsSat 8 Aug 2009
A non-static main instantiates an object of the class its in and then runs it. It's convenient in some cases where you have a manager or launcher class. But I think it does classify as a gotcha, so thanks for bringing it to the list.
andySun 9 Aug 2009
should a non-static main function the same as a static main?
Depends how you structure your code. As tatics points out, because you invoked open in your ctor and used an instance main, the ctor will get called twice, once to create a new instance, and then once more inside your main method.
Sticking the whole code block inside your main method, the behavoir will be the same for an instance and a static main method.
tompalmerMon 10 Aug 2009
I love the availability and convenience of non-static main, by the way. It does exactly what I want it to do.
tactics Tue 5 May 2009
Something we might want to keep track of is a list of Fan "Gotchas" for people coming from other languages. If you come up with any more, list them here:
Int[] list := Int[1024]
creates a list with a single element: the integer 1024. To do Java's equivalent ofnew int[1024]
in Fan, use theList.fill
method.:=
for initializing a variable, not=
.[,]
not[]
and the empty map is[:]
not[]
or{}
.MyThing.make.add(obj1).add(obj2).add(obj3)
. You need to make sure thatMyThing.add
has aThis
return type!make
and does not share the name of the class. It is marked with thenew
keyword in place of a return type.Obj x := 42; Str y = x;
compiles but throws a runtime error.Int
s corresponding to the character's Unicode code points. Be careful when appending characters to the end of a string! The code"Lol" +
s'' results in the string "Lol115", not "Lols" like you might expect. Use thetoChar
method to convertInt
s toStr
s.KevinKelley Tue 5 May 2009
Here's one that got me today...
to randomly pick 4 digits without repeating. Of course that gives a NullErr every now and then...
...since List.remove(Obj) is removing an Int that may or may not still be in the list; removeAt(index) is what I wanted.
Most of the time you'll get a compile error there, but not when the index and the content are of the same type.
tompalmer Wed 6 May 2009
Just tried it, and that's what I get, too. I personally think this should be changed. It should do repeated calls to add on the original object, in my opinion.
tompalmer Wed 6 May 2009
Or perhaps some kind of addAll, if it exists?
qualidafial Wed 6 May 2009
Wouldn't this break on a
const
class? i.e. MyImmutable.add may return a new MyImmutable that is identical save for the additional collection element.brian Wed 6 May 2009
Technically a list of expressions separated by a comma is a single expression itself which is why it makes sense to chain the adds together. That and it is more efficient. I debated this, and decided this is the right way (despite being a gotcha). BTW this is how normal list literals work too:
That would be a more expensive since you'd have to build a list first (just to destructure it again).
That is another reason I decided to make it a single chained expression. Although write now the result can't be assigned to anything, so we still can't really use it for immutables.
tactics Wed 6 May 2009
Additionally, it supports the case where a class has an "add()" function which returns a modified copy instead of using side effects. (Or at least, I think that's possible).
Gotchas are fine in a language as long as they don't do the opposite of what you expect.
Though in this case, if add() IS used in a comma expression, it might make sense to throw a more informative error. Right now, the error generated is triggered by the code after de-sugaring, but it doesn't seem too hard to do a check before de-sugaring.
qualidafial Wed 6 May 2009
Does the compiler generate an error if an add expression in an it-block is applied to a type where the
add
method returns something other thanThis
?brian Wed 6 May 2009
That is a good idea, I will add a meaningful error message.
tactics Wed 6 May 2009
It throws whatever errors the expansion would be. For example, if
MyThing.add
is defined as aVoid
-valued function, then with the code:The resulting expansion inside the compiler would be
And the result is an error message like "Cannot call
.add
on aVoid
-valued expression.alexlamsl Thu 7 May 2009
Would you mind elaborating on that point? I would expect
MyThing{obj1,obj2,obj3}
to work even with:JohnDG Thu 7 May 2009
The problem is in the expansion:
Desugars to:
So
with
is the one returning the value. I suppose it could return the return value of the it-block, which would make the example work, but then what to do for most it-blocks that have no need to return any value???alexlamsl Thu 7 May 2009
Ah I see - the sugar hypes me out, eventually >____<"
In this case, I guess with could make a judgement between
|This->Void|
and|This->This|
, with any other cases being illegal it-blocks.brian Fri 8 May 2009
I added a better error message.
qualidafial Fri 8 May 2009
Should it-blocks return
This
if they use the comma syntax to add items? This aligns with the requirement that theadd
method returnsThis
, and would enable alexlamsl's const usage above, thus:would desugar to:
Of course
sys::Obj.with
would then have to distinguish between it-blocks that returnVoid
(and in those cases returnthis
) and it-blocks that returnThis
(and in those cases return the result from the block). Not sure if this would be possible.brian Fri 8 May 2009
I don't think we can really do that without forcing everyone to return a value from a with-it-block (which would be annoying).
But you can solve the problem with immutable classes using a builder:
alexlamsl Fri 8 May 2009
It would be annoying only if you have to type out
return it
explicitly.Perhaps we can have
return it
as an implicit statement unless areturn
statement exists within the it-block?JohnDG Fri 8 May 2009
Self-return is a special and tedious case. It would be interesting to toy with the idea of auto returning it/this depending on the return type. Basically what
Void
does now.yachris Sat 8 Aug 2009
Here's a new gotcha (or maybe I'm an idiot :-)
I was looking at:
and refactoring towards more functionality (I wanted to bind a Button to a method, but that's irrelevant to this discussion) so I wanted to move the Window creation out to a method. So I did this:
Which seems reasonable, until you run it. It runs fine, but if you click the window's close box, the window comes back immediately! The second close of the window makes it go away for real. And if I use Command-Q (the mac's key-binding to quit a program) I get a lovely backtrace. For the record, I first saw the double-window-show at work on Windows, so it's not a Mac thing.
Finally, I did the following:
which (A) works and (B) makes sense, but... I'm confused. Why would the second one do the double-window thing? And should this be explained somewhere?
Thanks!
brian Sat 8 Aug 2009
I haven't tried to run your code, but I expect it is because your main is instance based and not static, so when you run main what you are really doing is this:
Try changing your main to static
yachris Sat 8 Aug 2009
Bingo! That fixed it. Thanks.
Raises the interesting question... should a non-static main function the same as a static main?
tactics Sat 8 Aug 2009
A non-static main instantiates an object of the class its in and then runs it. It's convenient in some cases where you have a manager or launcher class. But I think it does classify as a gotcha, so thanks for bringing it to the list.
andy Sun 9 Aug 2009
Depends how you structure your code. As tatics points out, because you invoked
open
in your ctor and used an instance main, the ctor will get called twice, once to create a new instance, and then once more inside yourmain
method.Sticking the whole code block inside your
main
method, the behavoir will be the same for an instance and a staticmain
method.tompalmer Mon 10 Aug 2009
I love the availability and convenience of non-static main, by the way. It does exactly what I want it to do.