You've already created at least one inconsistency, and a particularly damning one at that - treating functions differently from other values. A function declaration is conceptually the same as binding a variable to an anonymous function, but you're asserting that "regular" variables don't get hoisted while function declarations do. So...
f()
function f() { /* ... */ }
Works, but...
f()
var f = function() { /* ... */ } // Pretend this is anonymous function syntax
Doesn't. (And if you don't allow passing functions around like any other value, your language is horribly crippled.) Next, what happens if a function is redefined?
f()
function f() { /* definition 1 */ }
...
function f() { /* definition 2 */ }
If you treat this as an error, you're once again rejecting functions as values. There's no reason you shouldn't be able to do:
var f = function() { /* def 1 */ }
f = function() { /* def 2 */ }
So let's assume you'll allow the function to be redefined. How do you deal with lexical scoping?
function f() { /* definition 1 */ }
function bar() {
f()
function f() { /* definition 2 */ }
If you hoist definition 2 to the top of bar
's scope, you end up with this:
function f() { /* definition 1 */ }
function bar() {
function f() { /* definition 2 */ }
f()
Which means you can never call the outer definition from a nested scope unless you give the inner function a different name. Moreover, in any other language, the meaning of the above program is independent of the choice of name for definition 2. The hoisting rule changes the meaning depending on whether there's a name collision or not. On the other hand if you don't hoist definition 2 to the top of bar
, you've broken your own rule.
What is a type?
A type is meta data that can be used, at compile time or run time, to describe the shape of our data.
trait A { val b: X; def c(y: Y): Z}
So we know in our program that when ever we see an a: A
, we know that a
has the shape of A
so we can call a.b
safely.
So what shape does null
have?
null
has no data. null
does not have a shape. String s = null
is allowed as null
has no shape, so it can fit any where. MikeFHay above says that an empty collection can be thought of as a null object
. This is wrong, an empty collection has exactly the same shape as any other collection.
If a field can be null, then we can no longer rely on a.b
to be safe. The shape of the data in this field is not truly A
, only potentially A
. The other shape it can be is null
. That is the shape of this field is a union of A
and null
.
Most languages allow unions to be implemented as a tagged union
. Scala's Option
type is a tagged union of None | Some(a:A)
. Like wise Ceylon adds a ?
postfix for types that say that the type may be null
as in String? s = null
.
Best Answer
The shorter way is to architect your application so your variable already has
.isFlagBlaBla
, either by makingstate
already an instance ofStateA
, or by making.isFlagBlaBla
a method of the base class you're using. This is basic SOLID OOP design principles. If you post more context in another question, we could help you refactor.Aside from that, there's really no way to shorten this, other than extracting it into a function. Functional languages let you simplify almost anything by extracting it into a function. You can always write an abomination like the following:
Type casts are extremely rare in languages with type systems as powerful as Scala's. I've literally never had to do one in Scala, and didn't even remember the syntax. Even in languages with medium-strength type systems, they should be heavily scrutinized.