I'd like to make a humble recommendation of eliminating the === and !== operators. Might be too breaking at this point. My concern is how it introduces programmer burden to have to think about which one to use when in most cases they are either equivalent or the right choice is == (or !=). If enums make the == method final, then there's no issue there, either. The runtime could happily inline to pointer checks.
There may be some value in testing pointer equality still, but it should be rare enough to be more awkward. I recommend a static method such as Obj.same(a, b).
brianMon 16 Jun 2008
I think the notation of reference equality is pretty fundamental to any language, so I can't imagine living without it. Although unless you really need it, you shouldn't ever have to care and can always use == and !=. I suspect you've looked at some of my code where I've used === as an optimization when I know the values are interned. That is just a temporary thing because I haven't added those smarts to the compiler (where the smarts really belong).
Whether we use === or Obj.same, I personally prefer the operator rather than clutter up the Obj namespace. Although I'd be open to hear arguments for/against.
tompalmerMon 16 Jun 2008
Hmm. I agree that Obj isn't a nice place to add clutter. Maybe under Sys instead? I just think it would be nice to have == a lot more obvious than ===/'same'. Again, for easier decision making.
otomodachiMon 16 Jun 2008
I don't think that reference equality could be conceptually placed inside the referred object itself. IMHO the object can only tell the structural equality, so if we are into mapping the operators to methods (or vice versa), I would say structural equalty should be Obj.equals(that) and reference equality should be Sys.same(ref1, ref2).
I don't like either having two different operators looking almost identical (== and ===), so I would choose another operator shape for testing reference equality: I propose using is. This way it can be much more obvious whether you are checking for structural or reference equality.
BTW it would be great to have a "preview" button when posting :-)
andyMon 16 Jun 2008
is is already taken to check inheritance:
class A {}
class B : A {]
a := A.make
b := B.make
echo(b is A) => true
echo(a is B) => false
I personally like the === operator over a method call. Given its meaning is similar to the == operator, it sort of makes sense they look similar.
alexlamslTue 17 Jun 2008
I would like put a +1 vote for making reference equality a slot in Sys as well.
It feels analoguous to Java's System.identityHashCode() when retrieving the underlying Object.hashCode() after being overriden by a sub-class.
brianWed 18 Jun 2008
Sounds like a couple people think it belongs as Sys.same versus ===, and I can see that. I still lean towards the operator because:
as a systems language reference equality seems pretty important
the compiler can do comparison type checking
it efficiently compiles down to a fast opcode
But if it isn't used very often, then we probably don't need the complexity of an operator. When I add that optimization to the compiler I will go thru all the code and see how often we still need that operator - if it isn't used much then we can fallback to a method.
JohnDGWed 18 Jun 2008
I like ===, personally, as it's a standard in several other languages, and referential equality is both natively supported by the JVM and essential to many core algorithms.
tompalmerWed 18 Jun 2008
Brian, I like the "convert and see" strategy.
jodastephenThu 19 Jun 2008
I support keeping === as an operator for reference equality. It matches other languages and is easy to grasp. I definitely don't think it should be hidden away as a method.
Separately, I think that the is operator would be better as isa, as that is more descriptive of what it does. Saying that, if I were in charge, I'd just use instanceof for operator, as I think that is even more clear (and its part of Java I don't think is broken).
brianThu 19 Jun 2008
The is and as operators come straight from C#
alexlamslThu 19 Jun 2008
I would like to be educated on usages of as over is. On (somewhat rare) occasions when I use is / instanceof the branch logic tends to be more than return null.
Most of the time you use is (or instanceof) in conjunction with a cast:
if (foo is Bar)
{
bar := (Bar)foo
bar.doSomething()
}
Fan and C# combine the type check and cast into one operation:
bar := foo as Bar
if (bar != null) bar.doSomething()
In practice, I almost always use the as operator instead of the is operator.
alexlamslThu 19 Jun 2008
Good point. But how about for the following code?
if (foo is Bar) {
bar := (Bar) foo
bar.balance
} else if (foo is Car) {
car := (Car) foo;
car.accelerate
} else {
spin(foo)
}
andyThu 19 Jun 2008
Its probably mostly a matter of taste. I primarily use it when I know the type, since I find its cleaner than the cast (especially if inside parens):
f := ((Int)x).toHex
f := (x as Int).toHex
It also happens to map to a single opcode in the CLR.
brianThu 19 Jun 2008
Actually I would write that code (anything with more than one if/else) as a switch:
switch (foo.type)
{
case Bar.type: bar->balance
case Car.type: car->accelerate
default: spin(foo)
}
EDIT: actually that code won't work with inheritance (duh!). Probably if I had to write that code I'd use -> to keep the code cleaner:
if (foo is Bar)
foo->balance
else if (foo is Car)
foo->accelerate
else
spin(foo)
tompalmerFri 20 Jun 2008
A mix of thoughts. The compiler should be able to do autocasting in cases like this (though only more reliably than the duck calls in cases like local vars not modified in closures):
if (foo is Bar) {
foo.balance // The compiler can insert the cast because it knows the context.
} else ...
Also, if nullable ? is introduced, then we could change the meaning of as like so (and potentially get rid of other forms of explicit inline casting):
a := x as Int // throws NullErr if null and CastErr unless Int
b := x as Int? // same behavior as current Fan/C#
And finally, === doesn't mean the same thing in all languages that have it. In ECMAScript, it means "same value and same type" (as compared to == that does type conversion first such as between Numbers and Strings), but when applied to any user-defined Object types, it still effectively does pointer equality (and == does too for that matter). The differences between === and pointer equality are subtle in ES. In the end, I still think having both operators makes for harder decision making by programmers. But if it lives in the end, I don't think it will ruin the language.
I'm not saying that any of my above ideas are vital or perhaps even good (and it's not my language anyway), but I think they are worth thinking about.
(Side note: I'm terrible about proofreading before submitting. My individual edits don't send out multiple emails, do they? I just subscribe in batch mode.)
andyFri 20 Jun 2008
Only new posts queue an email - so no, your edits won't send out dups :)
tompalmerFri 20 Jun 2008
Thanks much for verifying that.
heliumFri 20 Jun 2008
I just want to mention what C++ allows:
if (Bar * bar = dynamic_cast<Bar*>(foo)) bar->doSomething()
which would look in Fan like this:
if (bar := foo as Bar) bar.doSomething()
The scope of the variable is only the coresponding if-branch.
alexlamslFri 20 Jun 2008
That one liner is really handy, especially if it works in Fan already!
I always thought the implicit casting within the instanceof if-block would be a handy feature for Java. But if we can fit that inside the condition, there is less point to introduce a feature that might introduce surprises.
tompalmerFri 20 Jun 2008
There could be some surprises for Bool values in there, but it does seem nice otherwise. Hmm.
brianSat 21 Jun 2008
if (bar := foo as Bar) bar.doSomething()
That is neat, but looks like it relies on the fact that any non-zero value is considered true in C/C++. In a language like Fan where Bar can never be evaluated as a Bool I'm not sure how it could work.
But this kind of reminds me of the null checking syntax sugar on the other thread. If you have a compact way to do express "do this if not null", then maybe that could be used like Helium's example.
tompalmerSat 21 Jun 2008
I actually love the "truthiness" style from languages like Python and ECMAScript where classes can be customized to be evaluated in boolean fashions (and note that this is very different from implicit conversion to boolean). But I'll save that subject for a different thread sometime.
otomodachiSat 21 Jun 2008
The previous example looks nicer with Groovy's Safe Navigation operator:
(foo as Bar)?.doSomething()
...doesn't it?
heliumSat 21 Jun 2008
OK, then an a bit more complicated example:
if (bar := foo as Bar)
whatever.doSomething(bar.someMethod())
How does Groovy handle this?
Of course this could conveniently be handled by monads. Something pseudo-Scala-like:
for (bar <- foo as Bar)
whatever.doSomething(bar.someMethod())
You could even handle a lot more complicated cases:
for (bar <- foo as Bar, qux <- baz as Qux)
bar.doSomething(qux.something())
But monads are hard to understand for most people. In this special case you could just imagine that all values are collections of zero (when it's null) or one element and the statement just iterates over these collections like nested for-each loops.
tompalmerSat 21 Jun 2008
I agree that Scala style outside most common experience. Option types and their behavior like Lists in Scala works well there, but I personally don't think it's good for Fan.
heliumSat 21 Jun 2008
Well, monads don't come from Scala but from category theory. The most well known use in programming languages is in Haskell. I just used Scala as an example, because it's a more Fan-like. Monads are very powerfull. In Haskell they're used for example for an exception system, for continuations, parser combinators, state, IO, ... . (Scala has (like most languags) built in exceptions and state so you don't use monads for that there.)
tompalmerSat 21 Jun 2008
I know monads go way back, not that I understand them all the way. Back a few years, I toyed with Haskell for a few days, and it was cool, but I never learned it well. I know Scala much better than other functional languages, so I reference that more often.
brianThu 26 Jun 2008
When I add that optimization to the compiler I will go thru all the code and see how often we still need that operator - if it isn't used much then we can fallback to a method.
Since this discussion, I've used the === operator several times - and I really did want to check reference equality, not the result of equals. So I'm going to make the call that the === and !== operators stay as is.
tompalmer Sat 14 Jun 2008
I'd like to make a humble recommendation of eliminating the
===
and!==
operators. Might be too breaking at this point. My concern is how it introduces programmer burden to have to think about which one to use when in most cases they are either equivalent or the right choice is==
(or!=
). If enums make the==
method final, then there's no issue there, either. The runtime could happily inline to pointer checks.There may be some value in testing pointer equality still, but it should be rare enough to be more awkward. I recommend a static method such as
Obj.same(a, b)
.brian Mon 16 Jun 2008
I think the notation of reference equality is pretty fundamental to any language, so I can't imagine living without it. Although unless you really need it, you shouldn't ever have to care and can always use
==
and!=
. I suspect you've looked at some of my code where I've used===
as an optimization when I know the values are interned. That is just a temporary thing because I haven't added those smarts to the compiler (where the smarts really belong).Whether we use
===
orObj.same
, I personally prefer the operator rather than clutter up the Obj namespace. Although I'd be open to hear arguments for/against.tompalmer Mon 16 Jun 2008
Hmm. I agree that Obj isn't a nice place to add clutter. Maybe under
Sys
instead? I just think it would be nice to have==
a lot more obvious than===
/'same'. Again, for easier decision making.otomodachi Mon 16 Jun 2008
I don't think that reference equality could be conceptually placed inside the referred object itself. IMHO the object can only tell the structural equality, so if we are into mapping the operators to methods (or vice versa), I would say structural equalty should be
Obj.equals(that)
and reference equality should beSys.same(ref1, ref2)
.I don't like either having two different operators looking almost identical (== and ===), so I would choose another operator shape for testing reference equality: I propose using
is
. This way it can be much more obvious whether you are checking for structural or reference equality.BTW it would be great to have a "preview" button when posting :-)
andy Mon 16 Jun 2008
is
is already taken to check inheritance:I personally like the
===
operator over a method call. Given its meaning is similar to the==
operator, it sort of makes sense they look similar.alexlamsl Tue 17 Jun 2008
I would like put a +1 vote for making reference equality a slot in Sys as well.
It feels analoguous to Java's System.identityHashCode() when retrieving the underlying Object.hashCode() after being overriden by a sub-class.
brian Wed 18 Jun 2008
Sounds like a couple people think it belongs as
Sys.same
versus===
, and I can see that. I still lean towards the operator because:But if it isn't used very often, then we probably don't need the complexity of an operator. When I add that optimization to the compiler I will go thru all the code and see how often we still need that operator - if it isn't used much then we can fallback to a method.
JohnDG Wed 18 Jun 2008
I like
===
, personally, as it's a standard in several other languages, and referential equality is both natively supported by the JVM and essential to many core algorithms.tompalmer Wed 18 Jun 2008
Brian, I like the "convert and see" strategy.
jodastephen Thu 19 Jun 2008
I support keeping === as an operator for reference equality. It matches other languages and is easy to grasp. I definitely don't think it should be hidden away as a method.
Separately, I think that the is operator would be better as isa, as that is more descriptive of what it does. Saying that, if I were in charge, I'd just use instanceof for operator, as I think that is even more clear (and its part of Java I don't think is broken).
brian Thu 19 Jun 2008
The
is
andas
operators come straight from C#alexlamsl Thu 19 Jun 2008
I would like to be educated on usages of
as
overis
. On (somewhat rare) occasions when I useis
/instanceof
the branch logic tends to be more thanreturn null
.For reference, the example given on MSDN isn't very convincing.
brian Thu 19 Jun 2008
Most of the time you use
is
(orinstanceof
) in conjunction with a cast:Fan and C# combine the type check and cast into one operation:
In practice, I almost always use the
as
operator instead of theis
operator.alexlamsl Thu 19 Jun 2008
Good point. But how about for the following code?
andy Thu 19 Jun 2008
Its probably mostly a matter of taste. I primarily use it when I know the type, since I find its cleaner than the cast (especially if inside parens):
It also happens to map to a single opcode in the CLR.
brian Thu 19 Jun 2008
Actually I would write that code (anything with more than one if/else) as a switch:
EDIT: actually that code won't work with inheritance (duh!). Probably if I had to write that code I'd use
->
to keep the code cleaner:tompalmer Fri 20 Jun 2008
A mix of thoughts. The compiler should be able to do autocasting in cases like this (though only more reliably than the duck calls in cases like local vars not modified in closures):
Also, if nullable
?
is introduced, then we could change the meaning ofas
like so (and potentially get rid of other forms of explicit inline casting):And finally,
===
doesn't mean the same thing in all languages that have it. In ECMAScript, it means "same value and same type" (as compared to==
that does type conversion first such as between Numbers and Strings), but when applied to any user-defined Object types, it still effectively does pointer equality (and==
does too for that matter). The differences between===
and pointer equality are subtle in ES. In the end, I still think having both operators makes for harder decision making by programmers. But if it lives in the end, I don't think it will ruin the language.I'm not saying that any of my above ideas are vital or perhaps even good (and it's not my language anyway), but I think they are worth thinking about.
(Side note: I'm terrible about proofreading before submitting. My individual edits don't send out multiple emails, do they? I just subscribe in batch mode.)
andy Fri 20 Jun 2008
Only new posts queue an email - so no, your edits won't send out dups :)
tompalmer Fri 20 Jun 2008
Thanks much for verifying that.
helium Fri 20 Jun 2008
I just want to mention what C++ allows:
which would look in Fan like this:
The scope of the variable is only the coresponding if-branch.
alexlamsl Fri 20 Jun 2008
That one liner is really handy, especially if it works in Fan already!
I always thought the implicit casting within the
instanceof
if-block would be a handy feature for Java. But if we can fit that inside the condition, there is less point to introduce a feature that might introduce surprises.tompalmer Fri 20 Jun 2008
There could be some surprises for
Bool
values in there, but it does seem nice otherwise. Hmm.brian Sat 21 Jun 2008
That is neat, but looks like it relies on the fact that any non-zero value is considered true in C/C++. In a language like Fan where
Bar
can never be evaluated as aBool
I'm not sure how it could work.But this kind of reminds me of the null checking syntax sugar on the other thread. If you have a compact way to do express "do this if not null", then maybe that could be used like Helium's example.
tompalmer Sat 21 Jun 2008
I actually love the "truthiness" style from languages like Python and ECMAScript where classes can be customized to be evaluated in boolean fashions (and note that this is very different from implicit conversion to boolean). But I'll save that subject for a different thread sometime.
otomodachi Sat 21 Jun 2008
The previous example looks nicer with Groovy's Safe Navigation operator:
...doesn't it?
helium Sat 21 Jun 2008
OK, then an a bit more complicated example:
How does Groovy handle this?
Of course this could conveniently be handled by monads. Something pseudo-Scala-like:
You could even handle a lot more complicated cases:
But monads are hard to understand for most people. In this special case you could just imagine that all values are collections of zero (when it's null) or one element and the statement just iterates over these collections like nested for-each loops.
tompalmer Sat 21 Jun 2008
I agree that Scala style outside most common experience. Option types and their behavior like Lists in Scala works well there, but I personally don't think it's good for Fan.
helium Sat 21 Jun 2008
Well, monads don't come from Scala but from category theory. The most well known use in programming languages is in Haskell. I just used Scala as an example, because it's a more Fan-like. Monads are very powerfull. In Haskell they're used for example for an exception system, for continuations, parser combinators, state, IO, ... . (Scala has (like most languags) built in exceptions and state so you don't use monads for that there.)
tompalmer Sat 21 Jun 2008
I know monads go way back, not that I understand them all the way. Back a few years, I toyed with Haskell for a few days, and it was cool, but I never learned it well. I know Scala much better than other functional languages, so I reference that more often.
brian Thu 26 Jun 2008
Since this discussion, I've used the
===
operator several times - and I really did want to check reference equality, not the result of equals. So I'm going to make the call that the===
and!==
operators stay as is.tompalmer Thu 26 Jun 2008
OK. Thanks for the update.