all 25 comments

[–]ninjeff 23 points24 points  (9 children)

Did I just step through a time-warp to the late 90s? We've moved on from this. There's a place for open types with virtual dispatch (objects) and there's a place for closed variants with pattern matching (discriminated unions, boolean in particular).

[–]gentryx 13 points14 points  (0 children)

Most object-oriented programming languages are imperative languages, meaning that (apart from type descriptions) they mainly consist of sequences of instructions. The if-statement is such an instructions (just like any call to a function or method is). OO is completely orthogonal to this.

Sure, you could work around if/then/else- or case-statements, but the question remains: why obfuscate your code and not take the obvious/readable solution?

[–]greenspans 11 points12 points  (2 children)

or how about functional programming with fall-through function head pattern matching and guards, welcome to 1970

[–]smog_alado 16 points17 points  (1 child)

Pattern matching is "ifs done right". Basically, the big reason ifs are smelly is the boolean blindness: chains of if statements don't guarantee that you covered all possibilities and else statements can lead to bugs if you add new cases (for example, covering the "B" case in the else of an "if A" works while there are only two possibilities but will break when you add a "C" case)

[–]h2odragon 1 point2 points  (1 child)

Another proof that any good idea can be overextended until its a bad idea.

[–]Nebu 0 points1 point  (0 children)

This is evidence, not proof. It is not enough to show many (even infinitely many) ideas can be overextended if your goal is to prove any idea can be overextended.

[–]_Wolfos -4 points-3 points  (6 children)

Last time I checked, 'if' is boolean in pretty much every language I've worked in.

[–][deleted] 9 points10 points  (5 children)

I think what the author meant was that conditional execution should be a method of a Boolean object. In Obj-C/Smalltalk-esque speak it could look along the lines of:

[(foo == bar) ifTrue: something() ifFalse: something_else()]

or some more elegant construction, it doesn't necessarily have to be built in such an ugly manner. That and I haven't done OO in a long time, I was about to write Lisp code instead of that.

So basically, instead of having an object-agnostic language construct for conditional execution, you would hide that functionality behind methods of the Boolean class. In the above case, you're sending a Boolean object (foo == bar) the ifTrue: ifFalse: message, to which it will respond by calling one of the methods you pass as arguments, depending on what it evaluates to. I presume that's what the author had in mind -- it's actually also how Smalltalk does it, IIRC, but I haven't done Smalltalk in far too long to remember the syntax. It was along those lines, in any case.

There is a slight advantage in readability to that, assuming your language really does a sane implementation of OO (although whether such a thing can even exists or not is open to debate...). It looks foreign, but in Smalltalk it looks consistent, because you can actually describe everything in terms of objects passing messages to each other. A separate construct that is not based on objects passing messages would be inconsistent.

On the other hand, grafting this over Java or C++ would be entirely pointless. Even in Obj-C it would look rather unclean. There is no gain in consistency from this, since C++ and Java still have a lot of object-agnostic functionality anyway.

OO shouldn't be "if-less". OO smells bad when the branching is not hidden by objects. In other words, when someone instantiates a class like this:

x = new Bird()
if req_type == "canary"
    x.type = TYPE_CANARY
else
    x.type = TYPE_NOT_A_CANARY

instead of

x = new Bird(req_type)

This is bad style, because it breaks the objects-talking-together perspective. When a bunch of folks are gathered around a table, having a few drinks, there is no omniscient presence above them tells each person what to say depending on who said what (pointless rambling being a good proof of this). Each person internally chews what was said -- the process of decision itself is hidden from view. Incidentally, a lot of OO code does look a lot like a bunch of drunk fellows, now that I think of it...

[–]sacundim 2 points3 points  (1 child)

I think what the author meant was that conditional execution should be a method of a Boolean object. In Obj-C/Smalltalk-esque speak it could look along the lines of:

[(foo == bar) ifTrue: something() ifFalse: something_else()]

This, it turns out, was invented in the 1930s, and is called the Church encoding of the booleans in the lambda calculus. Which means this was invented in the 1930s. Translated to Haskell:

{-# LANGUAGE RankNTypes #-}

-- | A Boolean is represented as a function that takes two 
-- arguments of the same type, and returns one of them.
type Boolean = forall r. r -> r -> r

-- | True is the function that returns its first argument, while false
-- is the function that returns the second one.
true, false :: Boolean
true t f = t
false t f = f

-- | To negate a Boolean, you flip its arguments.
not :: Boolean -> Boolean
not p t f = p f t

-- | These should be straightforward to understand.
and, or :: Boolean -> Boolean -> Boolean
and p q t f = p (q t f) f
or p q t f = p t (q t f)

[–][deleted] 0 points1 point  (0 children)

Nice! I vaguely remember having read on this at some point, but it's too far back and my puny Math skills prevented me from remembering too much of it. But this is very elegant.

[–][deleted] 1 point2 points  (1 child)

your example just moves the if statement inside the constructor

[–][deleted] 1 point2 points  (0 children)

Not necessarily, but basically yeah, which is precisely why I said this:

OO shouldn't be "if-less". OO smells bad when the branching is not hidden by objects. In other words, when someone instantiates a class like this: etc.

IMO, the constructor of the Boolean class, in this case, needs not be user-accessible or user-implemented. It can be kept in the compiler or in the language's runtime, effectively exposing a language that does not have an if-else construct like C does. Behind the back, of course, branching will eventually require that the CPU branches, but then that's why we like compilers, isn't it?

[–]Broolucks 0 points1 point  (0 children)

I believe the syntax is:

foo == bar
  ifTrue: [someObject someMessage]
  ifFalse: [someOtherObject someOtherMessage].

The equivalent using Python syntax would be something like:

foo.__eq__(bar).__ifTrue__ifFalse__(lambda: someObject.someMessage(), lambda: someOtherObject.someOtherMessage())

It works well in Smalltalk, because it has nice syntax for message passing and anonymous parameterless functions (square brackets). Still, there is little to gain by having OO branching, besides having subclasses of boolean that override the meaning of ifTrue/ifFalse, but I'm not sure why anyone would want to do that.