Forgive me if this is a kind of silly question, but I've been going through "Programming Elm" and one thing struck me as a little odd: in the text he shows an example of creating a record,
dog = { name = "Tucker", age = 11 }
and then right after that he shows a function that returns a record
haveBirthday d = { name = d.name, age = d.age + 1 }
To me, the syntax for both seems remarkably similar. How does the compiler know which is which? By the + on the right hand side of the function, that implies change, so it has to be a function? By the fact that there's an argument, d? Or is it that the difference between generating a record and a function is quite obvious, and it's just in this case that they seem so alike? Or is it that in some subtle way that I don't yet have the Zen to grasp, they are in fact the same thing? (That is, something like "everything is a function"?)
I've looked at https://elm-lang.org/docs/syntax#functions -- the docs are very user-friendly, but brief. Are there any other resources that give a more didactic view of the syntax (like this book does for Haskell)?
Thanks for any help along the way.
In an imperative language where "side-effects" are the norm, the term "function" is often used to describe what's more appropriately called a procedure or sub-routine. A set of instruction to be executed when called, and where order of execution and re-evaluation is essential because mutation and other side-effects can change anything from anywhere at any time.
In functional programming, however, the notion of a function is closer to the mathematical sense of the term, where its return value is computed entirely based on its arguments. This is especially true for a "pure" functional language like Elm, which normally does not allow "side-effects". That is, effects that interact with the "outside world" without going through the input arguments or return value. In a pure functional language it does not make sense to have a function that does not take any arguments, because it would always do the same thing, and computing the same value again and again is just wasteful. A function with no arguments is effectively just a value. And a function definition and value binding can therefore be distinguished solely based on whether or not it has any arguments.
But there are also many hybrid programming languages. Most functional languages are hybrids in fact, that allow side-effects but still stick close to the mathematical sense of a function. These languages also typically don't have functions without arguments, but use a special type called unit, or (), which has only one value, also called unit or (), which is used to denote a function that takes no significant input, or which returns nothing significant. Since unit has only one value, it carries no significant information.
Many functional languages don't even have functions that take multiple arguments either. In Elm and many other languages, a function takes exactly one argument. No more and no less, ever. You might have seen Elm code which appears to have multiple arguments, but that's all an illusion. Or syntax sugar as it's called in language theoretic lingo.
When you see a function definition like this:
add a b = a + b
that actual translates to this:
add = \a -> \b -> a + b
A function that takes an argument a, then returns another function which takes an argument b, which does the actual computation and returns the result. This is called currying.
Why do this? Because it makes it very convenient to partially apply functions. You can just leave out the last, or last few, arguments, then instead of an error you get a function back which you can fully apply later to get the result. This enables you to do some really handy things.
Let's look at an example. To fully apple add from above we'd just do:
add 2 3
The compiler actually parses this as (add 2) 3, so we've kind of done partial application already, but then immediately applied to another value. But what if we want to add 2 to a whole bunch of things and don't want write add 2 everywhere, because DRY and such? We write a function:
add2ToThings thing =
add 2 thing
(Ok, maybe a little bit contrived, but stay with me)
Partial application allows us to make this even shorter!
add2ToThings =
add 2
You see how that works? add 2 returns a function, and we just give that a name. There have been numerous books written about this marvellous idea in OOP, but they call it "dependency injection" and it's usually slightly more verbose when implemented with OOP techniques.
Anyway, say we have a list of "thing"s, we can get a new list with 2 added to everything by mapping over it like this:
List.map add2ToThings things
But we can do even better! Since add 2 is actually shorter than the name we gave it, we might as well just use it directly:
List.map (add 2) things
Ok, but then say we want to filter out every value that is exactly 5. We can actually partially apply infix operators too, but we have to surround the operator in parentheses to make it behave like an ordinary function:
List.filter ((/=) 5) (List.map (add 2) things)
This is starting to look a bit convoluted though, and reads backwards since we filter after we map. Fortunately we can use Elm's pipe operator |> to clean it up a bit:
things
|> List.map (add 2)
|> List.filter ((/=) 5)
The pipe operator was "discovered" because of partial application. Without that it couldn't have been implemented as an ordinary operator, but would have to be implemented as a special syntax rule in the parser. It's implementation is (essentially) just:
x |> f = f x
It takes an arbitrary argument on its left side and a function on its right side, then applies the function to the argument. And because of partial application we can conveniently get a function to pass in on the right side.
So in three lines of ordinary idiomatic Elm code we've used partial application four times. Without that, and currying, we'd have to write something like:
List.filter (\thing -> thing /= 5) (List.map (\thing -> add 2 thing) things)
Or we might want to write it with some variable bindings to make it more readable:
let
add2ToThings thing =
add 2 thing
thingsWith2Added =
List.map add2ToThings things
thingsWith2AddedAndWithout5 =
List.filter (\thing -> thing /= 5) thingWith2Added
in
thingsWith2AddedAndWithout5
And so that's why functional programming is awesome.
In Java Streams - what is the difference between stream.max(Comparator) and stream.collect(Collectors.maxBy(Comparator)) in terms of preformance. Both will fetch the max based on the comparator being passed. If this is the case why do we need the additional step of collecting using the collect method? When should we choose former vs latter? What are the use case scenarios suited for using both?
They do the same thing, and share the same code.
why do we need the additional step of collecting using the collect method?
You don't. Use max() if that's what you want to do. But there are cases where a Collector can be handy. For example:
Optional<Foo> result = stream.collect(createCollector());
where createCollector() would return a collector based on some condition, which could be maxBy, minBy, or something else.
In general, you shouldn't care too much about the small performance differences that might exist between two methods that do the same thing, and have a huge chance of being implemented the same way. Instead, you should make your code as clear and readable as possible.
There is a relevant quote in Effective Java 3rd Edition, page 214:
The collectors returned by the counting method are intended only for use as downstream collectors. The same functionality is available directly on Stream, via the count method, so there is never a reason to say collect(counting()). There are fifteen more Collectors with this property.
Given that maxBy is duplicated by Stream.max, it is presumably one of these sixteen methods.
Shortly after, same page, it goes on to justify the dual existence:
From a design perspective, these collectors represent an attempt to partially duplicate the functionality of streams in collectors so that downstream collectors can act as "ministreams".
Personally, I find this edict and explanation a bit unsatisfying: it says that it wasn't the intent for these 16 collectors to be used like this, but not why they shouldn't.
I suppose that the methods directly on stream are able to be implemented in specialized ways which could be more efficient than the general collectors.
According to java Documentation ,
the below are definition for maxBy , minBy From Collectors class ,
static <T> Collector<T,?,Optional<T>> maxBy(Comparator<? super T> comparator)
Returns a Collector that produces the maximal element according to a given Comparator, described as an Optional<T>.
static <T> Collector<T,?,Optional<T>> minBy(Comparator<? super T> comparator)
Returns a Collector that produces the minimal element according to a given Comparator, described as an Optional<T>.
where as max() and min() in Stream return the Optional<T>
every stream pipeline operation can be divided into terminal and non terminal operation .
so by definition from java doc , it is one thing clear that Stream provided max() ,min() are terminal operation and return Optional<T> .
but the maxBy() and minBy() are Collector producing operation , so they can be used for chaining computation .
They both use BinaryOperator.maxBy(comparator) and do a reducing operation to the elements (even though the implementation of how it is reduced is slightly different). Hence there are no changes in the output.
If you need to find the max among all the stream elements, I suggest using Stream.max because the code would look neat and also you do not really need to create a collector in this case.
But there are scenarios where Collectors.maxBy need to be used. Assume that you need to group your elements and need to find the max in each group. In such scenarios you cannot use Stream.max. Here you need to use Collectors.groupingBy(mapper, Collectors.maxBy(...)). Similarly you could use it for partitionBy and other similar methods where you need a collector.
I was having a look at the Arrow library found here. Why would ever want to use an Option type instead of Kotlin's built in nullables?
I have been using the Option data type provided by Arrow for over a year, and there at the beginning, we did the exact same question to ourselves. The answer follows.
Option vs Nullable
If you compare just the option data type with nullables in Kotlin, they are almost even. Same semantics (there is some value or not), almost same syntax (with Option you use map, with nullables you use safe call operator).
But when using Options, you enable the possibility to take benefits from the arrow ecosystem!
Arrow ecosystem (functional ecosystem)
When using Options, you are using the Monad Pattern. When using the monad pattern with libraries like arrow, scala cats, scalaz, you can take benefits from several functional concepts. Just 3 examples of benefits (there is a lot more than that):
1. Access to other Monads
Option is not the only one! For instance, Either is a lot useful to express and avoid to throw Exceptions. Try, Validated and IO are examples of other common monads that help us to do (in a better way) things we do on typical projects.
2. Conversion between monads + abstractions
You can easily convert one monad to another. You have a Try but want to return (and express) an Either? Just convert to it. You have an Either but doesn't care about the error? Just convert to Option.
val foo = Try { 2 / 0 }
val bar = foo.toEither()
val baz = bar.toOption()
This abstraction also helps you to create functions that doesn't care about the container (monad) itself, just about the content. For example, you can create an extension method Sum(anyContainerWithBigDecimalInside, anotherContainerWithBigDecimal) that works with ANY MONAD (to be more precise: "to any instance of applicative") this way:
fun <F> Applicative<F>.sum(vararg kinds: Kind<F, BigDecimal>): Kind<F, BigDecimal> {
return kinds.reduce { kindA, kindB ->
map(kindA, kindB) { (a, b) -> a.add(b) }
}
}
A little complex to understand, but very helpful and easy to use.
3. Monad comprehensions
Going from nullables to monads is not just about changing safe call operators to map calls. Take a look at the "binding" feature that arrow provides as the implementation of the pattern "Monad Comprehensions":
fun calculateRocketBoost(rocketStatus: RocketStatus): Option<Double> {
return binding {
val (gravity) = rocketStatus.gravity
val (currentSpeed) = rocketStatus.currentSpeed
val (fuel) = rocketStatus.fuel
val (science) = calculateRocketScienceStuff(rocketStatus)
val fuelConsumptionRate = Math.pow(gravity, fuel)
val universeStuff = Math.log(fuelConsumptionRate * science)
universeStuff * currentSpeed
}
}
All the functions used and also the properties from rocketStatus parameter in the above example are Options. Inside the binding block, the flatMap call is abstracted for us. The code is a lot easier to read (and write) and you don't need to check if the values are present, if some of them is not, the computation will stop and the result will be an Option with None!
Now try to imagine this code with null verifications instead. Not just safe call operators but also probably if null then return code paths. A lot harder isn't it?
Also, the above example uses Option but the true power about monad comprehensions as an abstraction is when you use it with monads like IO in which you can abstract asynchronous code execution in the exact same "clean, sequential and imperative" way as above :O
Conclusion
I strongly recommend you to start using monads like Option, Either, etc as soon as you see the concept fits the semantics you need, even if you are not sure if you will take the other big benefits from the functional ecosystem or if you don't know them very well yet. Soon you'll be using it without noticing the learning-curve. In my company, we use it in almost all Kotlin projects, even in the object-oriented ones (which are the majority).
Disclaimer: If you really want to have a detailed talk about why Arrow is useful, then please head over to https://soundcloud.com/user-38099918/arrow-functional-library and listen to one of the people who work on it. (5:35min)
The people who create and use that library simple want to use Kotlin differently than the people who created it and use "the Option datatype similar to how Scala, Haskell and other FP languages handle optional values".
This is just another way of defining return types of values that you do not know the output of.
Let me show you three versions:
nullability in Kotlin
val someString: String? = if (condition) "String" else null
object with another value
val someString: String = if (condition) "String" else ""
the Arrow version
val someString: Option<String> = if (condition) Some("String") else None
A major part of Kotlin logic can be to never use nullable types like String?, but you will need to use it when interopting with Java. When doing that you need to use safe calls like string?.split("a") or the not-null assertion string!!.split("a").
I think it is perfectly valid to use safe calls when using Java libraries, but the Arrow guys seem to think different and want to use their logic all the time.
The benefit of using the Arrow logic is "empowering users to define pure FP apps and libraries built atop higher order abstractions. Use the below list to learn more about Λrrow's main features".
One thing other answers haven't mentioned: you can have Option<Option<SomeType>> where you can't have SomeType??. Or Option<SomeType?>, for that matter. This is quite useful for compositionality. E.g. consider Kotlin's Map.get:
abstract operator fun get(key: K): V?
Returns the value corresponding to the given key, or null if such a key is not present in the map.
But what if V is a nullable type? Then when get returns null it can be because the map stored a null value for the given key or because there was no value; you can't tell! If it returned Option<V>, there wouldn't be a problem.
Since '+', '-', and the rest of the arithmetic operators must be, at heart, just function calls (I think), how are they defined? More specifically, how are they written such that they can look for arguments that precede them and follow them?
For example, the function '*', or multiply, in the expression 7 * 9 must look for the first argument 7 before it's called, and then the 9, it's second argument, which appears to be in the right place.
Most languages (not OCaml) require parentheses around arguments -- how have they gotten around this requirement in languages that do?
It sounds like you're asking about operator overloading, or at least reading into operator overloading will give you a pretty solid understanding on how arithmetic operations are defined in some languages. Here's a link to a nice tutorial on C++ operator overloading:
http://www.tutorialspoint.com/cplusplus/cpp_overloading.htm
In object-oriented world, this is called "operator overloading". It is just a kind of syntactic sugar, that enables you to call a + b, instead of a.+(b), that is actually the same method call.
This site tickled my sense of humour - http://www.antiifcampaign.com/ but can polymorphism work in every case where you would use an if statement?
Smalltalk, which is considered as a "truly" object oriented language, has no "if" statement, and it has no "for" statement, no "while" statement. There are other examples (like Haskell) but this is a good one.
Quoting Smalltalk has no “if” statement:
Some of the audience may be thinking
that this is evidence confirming their
suspicions that Smalltalk is weird,
but what I’m going to tell you is
this:
An “if” statement is an abomination in an Object Oriented language.
Why? Well, an OO language is composed
of classes, objects and methods, and
an “if” statement is inescapably none
of those. You can’t write “if” in an
OO way. It shouldn’t exist.
Conditional execution, like everything
else, should be a method. A method of
what? Boolean.
Now, funnily enough, in Smalltalk,
Boolean has a method called
ifTrue:ifFalse: (that name will look
pretty odd now, but pass over it for
now). It’s abstract in Boolean, but
Boolean has two subclasses: True and
False. The method is passed two blocks
of code. In True, the method simply
runs the code for the true case. In
False, it runs the code for the false
case. Here’s an example that hopefully
explains:
(x >= 0) ifTrue: [
'Positive'
] ifFalse: [
'Negative'
]
You should be able to see ifTrue: and
ifFalse: in there. Don’t worry that
they’re not together.
The expression (x >= 0) evaluates to
true or false. Say it’s true, then we
have:
true ifTrue: [
'Positive'
] ifFalse: [
'Negative'
]
I hope that it’s fairly obvious that
that will produce ‘Positive’.
If it was false, we’d have:
false ifTrue: [
'Positive'
] ifFalse: [
'Negative'
]
That produces ‘Negative’.
OK, that’s how it’s done. What’s so
great about it? Well, in what other
language can you do this? More
seriously, the answer is that there
aren’t any special cases in this
language. Everything can be done in an
OO way, and everything is done in an
OO way.
I definitely recommend reading the whole post and Code is an object from the same author as well.
That website is against using if statements for checking if an object has a specific type. This is completely different from if (foo == 5). It's bad to use ifs like if (foo instanceof pickle). The alternative, using polymorphism instead, promotes encapsulation, making code infinitely easier to debug, maintain, and extend.
Being against ifs in general (doing a certain thing based on a condition) will gain you nothing. Notice how all the other answers here still make decisions, so what's really the difference?
Explanation of the why behind polymorphism:
Take this situation:
void draw(Shape s) {
if (s instanceof Rectangle)
//treat s as rectangle
if (s instanceof Circle)
//treat s as circle
}
It's much better if you don't have to worry about the specific type of an object, generalizing how objects are processed:
void draw(Shape s) {
s.draw();
}
This moves the logic of how to draw a shape into the shape class itself, so we can now treat all shapes the same. This way if we want to add a new type of shape, all we have to do is write the class and give it a draw method instead of modifying every conditional list in the whole program.
This idea is everywhere in programming today, the whole concept of interfaces is all about polymorphism. (Shape is an interface defining a certain behavior, allowing us to process any type that implements the Shape interface in our method.) Dynamic programming languages take this even further, allowing us to pass any type that supports the necessary actions into a method. Which looks better to you? (Python-style pseudo-code)
def multiply(a,b):
if (a is string and b is int):
//repeat a b times.
if (a is int and b is int):
//multiply a and b
or using polymorphism:
def multiply(a,b):
return a*b
You can now use any 2 types that support the * operator, allowing you to use the method with types that haven't event been created yet.
See polymorphism and what is polymorhism.
Though not OOP-related: In Prolog, the only way to write your whole application is without if statements.
Yes actually, you can have a turing-complete language that has no "if" per se and only allows "while" statements:
http://cseweb.ucsd.edu/classes/fa08/cse200/while.html
As for OO design, it makes sense to use an inheritance pattern rather than switches based on a type field in certain cases... That's not always feasible or necessarily desirable though.
#ennuikiller: conditionals would just be a matter of syntactic sugar:
if (test) body; is equivalent to x=test; while (x) {x=nil; body;}
if-then-else is a little more verbose:
if (test) ifBody; else elseBody;
is equivalent to
x = test; y = true;
while (x) {x = nil; y = nil; ifBody;}
while (y) {y = nil; elseBody;}
the primitive data structure is a list of lists. you could say 2 scalars are equal if they are lists of the same length. you would loop over them simultaneously using the head/tail operators and see if they stop at the same point.
of course that could all be wrapped up in macros.
The simplest turing complete language is probably iota. It contains only 2 symbols ('i' and '*').
Yep. if statements imply branches which can be very costly on a lot of modern processors - particularly PowerPC. Many modern PCs do a lot of pipeline re-ordering and so branch mis-predictions can cost an order of >30 cycles per branch miss.
On console programming it's sometimes faster to just execute the code and ignore it than check if you should execute it!
Simple branch avoidance in C:
if (++i >= 15)
{
i = 0;
)
can be re-written as
i = (i + 1) & 15;
However, if you want to see some real anti-if fu then read this
Oh and on the OOP question - I'll replace a branch mis-prediction with a virtual function call? No thanks....
The reasoning behind the "anti-if" campaign is similar to what Kent Beck said:
Good code invariably has small methods and
small objects. Only by factoring the system into many small pieces of state
and function can you hope to satisfy the “once and only once” rule. I get lots
of resistance to this idea, especially from experienced developers, but no one
thing I do to systems provides as much help as breaking it into more pieces.
If you don't know how to factor a program with composition and inheritance, then your classes and methods will tend to grow bigger over time. When you need to make a change, the easiest thing will be to add an IF somewhere. Add too many IFs, and your program will become less and less maintainable, and still the easiest thing will be to add more IFs.
You don't have to turn every IF into an object collaboration; but it's a very good thing when you know how to :-)
You can define True and False with objects (in a pseudo-python):
class True:
def if(then,else):
return then
def or(a):
return True()
def and(a):
return a
def not():
return False()
class False:
def if(then,else):
return false
def or(a):
return a
def and(a):
return False()
def not():
return True()
I think it is an elegant way to construct booleans, and it proves that you can replace every if by polymorphism, but that's not the point of the anti-if campaign. The goal is to avoid writing things such as (in a pathfinding algorithm) :
if type == Block or type == Player:
# You can't pass through this
else:
# You can
But rather call a is_traversable method on each object. In a sense, that's exactly the inverse of pattern matching. "if" is useful, but in some cases, it is not the best solution.
I assume you are actually asking about replacing if statements that check types, as opposed to replacing all if statements.
To replace an if with polymorphism requires a method in a common supertype you can use for dispatching, either by overriding it directly, or by reusing overridden methods as in the visitor pattern.
But what if there is no such method, and you can't add one to a common supertype because the super types are not maintained by you? Would you really go to the lengths of introducing a new supertype along with subtypes just to get rid of a single if? That would be taking purity a bit far in my opinion.
Also, both approaches (direct overriding and the visitor pattern) have their disadvantages: Overriding the method directly requires that you implement your method in the classes you want to switch on, which might not help cohesion. On the other hand, the visitor pattern is awkward if several cases share the same code. With an if you can do:
if (o instanceof OneType || o instanceof AnotherType) {
// complicated logic goes here
}
How would you share the code with the visitor pattern? Call a common method? Where would you put that method?
So no, I don't think replacing such if statements is always an improvement. It often is, but not always.
I used to write code a lot as the recommend in the anti-if campaign, using either callbacks in a delegate dictionary or polymorphism.
It's quite a beguiling argument, especially if you are dealing with messy code bases but to be honest, although it's great for a plugin model or simplifying large nested if statements, it does make navigating and readability a bit of a pain.
For example F12 (Go To Definition) in visual studio will take you to an abstract class (or, in my case an interface definition).
It also makes quick visual scanning of a class very cumbersome, and adds an overhead in setting up the delegates and lookup hashes.
Using the recommendations put forward in the anti-if campaign as much as they appear to be recommending looks like 'ooh, new shiny thing' programming to me.
As for the other constructs put forward in this thread, albeit it has been done in the spirit of a fun challenge, are just substitutes for an if statement, and don't really address what the underlying beliefs of the anti-if campaign.
You can avoid ifs in your business logic code if you keep them in your construction code (Factories, builders, Providers etc.). Your business logic code would be much more readable, easier to understand or easier to maintain or extend. See: http://www.youtube.com/watch?v=4F72VULWFvc
Haskell doesn't even have if statements, being pure functional. ;D
You can do it without if per se, but you can't do it without a mechanism that allows you to make a decision based on some condition.
In assembly, there's no if statement. There are conditional jumps.
In Haskell for instance, there's no explicit if, instead, you define a function multiple times, I forgot the exact syntax, but it's something like this:
pseudo-haskell:
def posNeg(x < 0):
return "negative"
def posNeg(x == 0):
return "zero"
def posNeg(x):
return "positive"
When you call posNeg(a), the interpreter will look at the value of a, if it's < 0 then it will choose the first definition, if it's == 0 then it will choose the second definition, otherwise it will default to the third definition.
So while languages like Haskell and SmallTalk don't have the usual C-style if statement, they have other means of allowing you to make decisions.
This is actually a coding game I like to play with programming languages. It's called "if we had no if" which has its origins at: http://wiki.tcl.tk/4821
Basically, if we disallow the use of conditional constructs in the language: no if, no while, no for, no unless, no switch etc.. can we recreate our own IF function. The answer depends on the language and what language features we can exploit (remember using regular conditional constructs is cheating co no ternary operators!)
For example, in tcl, a function name is just a string and any string (including the empty string) is allowed for anything (function names, variable names etc.). So, exploiting this we can do:
proc 0 {true false} {uplevel 1 $false; # execute false code block, ignore true}
proc 1 {true false} {uplevel 1 $true; # execute true code block, ignore flase}
proc _IF {boolean true false} {
$boolean $true $false
}
#usage:
_IF [expr {1<2}] {
puts "this is true"
} {
#else:
puts "this is false"
}
or in javascript we can abuse the loose typing and the fact that almost anything can be cast into a string and combine that with its functional nature:
function fail (discard,execute) {execute()}
function pass (execute,discard) {execute()}
var truth_table = {
'false' : fail,
'true' : pass
}
function _IF (expr) {
return truth_table[!!expr];
}
//usage:
_IF(3==2)(
function(){alert('this is true')},
//else
function(){alert('this is false')}
);
Not all languages can do this sort of thing. But languages I like tend to be able to.
The idea of polymorphism is to call an object without to first verify the class of that object.
That doesn't mean the if statement should not be used at all; you should avoid to write
if (object.isArray()) {
// Code to execute when the object is an array.
} else if (object.inString()) {
// Code to execute if the object is a string.
}
It depends on the language.
Statically typed languages should be able to handle all of the type checking by sharing common interfaces and overloading functions/methods.
Dynamically typed languages might need to approach the problem differently since type is not checked when a message is passed, only when an object is being accessed (more or less). Using common interfaces is still good practice and can eliminate many of the type checking if statements.
While some constructs are usually a sign of code smell, I am hesitant to eliminate any approach to a problem apriori. There may be times when type checking via if is the expedient solution.
Note: Others have suggested using switch instead, but that is just a clever way of writing more legible if statements.
Well, if you're writing in Perl, it's easy!
Instead of
if (x) {
# ...
}
you can use
unless (!x){
# ...
}
;-)
In answer to the question, and as suggested by the last respondent, you need some if statements to detect state in a factory. At that point you then instantiate a set of collaborating classes that solve the state specific problem. Of course, other conditionals would be required as needed, but they would be minimized.
What would be removed of course would be the endless procedural state checking rife in so much service based code.
Interesting smalltalk is mentioned, as that's the language I used before being dragged across into Java. I don't get home as early as I used to.
I thought about adding my two cents: you can optimize away ifs in many languages where the second part of a boolean expression is not evaluated when it won't affect the result.
With the and operator, if the first operand evaluates to false, then there is no need to evaluate the second one. With the or operator, it's the opposite - there's no need to evaluate the second operand if the first one is true. Some languages always behave like that, others offer an alternative syntax.
Here's an if - elseif - else code made in JavaScript by only using operators and anonymous functions.
document.getElementById("myinput").addEventListener("change", function(e) {
(e.target.value == 1 && !function() {
alert('if 1');
}()) || (e.target.value == 2 && !function() {
alert('else if 2');
}()) || (e.target.value == 3 && !function() {
alert('else if 3');
}()) || (function() {
alert('else');
}());
});
<input type="text" id="myinput" />
This makes me want to try defining an esoteric language where blocks implicitly behave like self-executing anonymous functions and return true, so that you would write it like this:
(condition && {
action
}) || (condition && {
action
}) || {
action
}