re: Programming Paradigms and the Procedural Paradox VIEW POST

VIEW FULL DISCUSSION
 

This post has led me on a journey of discovery more about "pure" OO. In particular, the SmallTalk way. And what I saw standing out most with pure OO is the avoidance of control structures like if, case/switch (and additionally for).

This floored me at first, because I use if and case in functional programming. Case (match in F#) statements are basically required to deal with union types. Eventually I came to this conclusion: what is wrong with control structures (in OO languages) is that they require side effects, and they make it easy to mix side effects.

In (nearly all?) OO languages, there is no way to return a value from an if or switch statement except by mutation of something outside its scope. And if you have to add something to code which already mutates one thing outside of its scope, why not another? I'm not just theory-crafting here. I have seen and done this myself many times. This pattern / problem is basically borrowed from procedural programming.

string s = null;
if (Regex.IsMatch(input, somePattern)) {
    // must mutate s
    s = input;
    // why not also mutate something else ?
    ...
}
else
{
    // mutating s here too... who warns me if I forget?
    s = String.Empty;
}

Many people would replace this with the ternary operator precisely to avoid mixing mutations. But that falls apart if there are more than 2 cases.

I do not have an elegant solution in mind to do this in pure OO, or I would include that code here.

In functional programming if and case statements must return values. Some (most?) functional languages allow implicit side effects, so if can still be used for evil. But the methodology (and often the language) discourages you from doing so.

// no mutation
let s =
    if Regex.IsMatch(input, somePattern) then
        input
    else
        String.Empty

In all I wish I had found out about pure OO when I started programming. The way I actually did OO was procedural with objects, and found that it failed me. I think it underlines the point that following the methodology of a given paradigm matters more than using its features. I have certainly found this to be true of functional programming as well.

 

This boils down more to the expression vs. statement idea. The smallest atomic building block of the procedural style is the statement: a unit of code that changes the value of the global, implicit state. The smallest atomic building block of the functional style is an expression: a unit of code that expresses a computation that returns a value.

Most languages these days have a pretty arbitrary mix of statements and expressions. For example, in Java, "1 + 1" is an expression, as is "a = 2" (which is why you can write "b = a = 2"), but "if {}" is a statement, so you can't write "a = if {}". "Functional" languages tend to "cheat" and say everything is an expression, but some expressions don't return a value, i.e. return some placeholder like nil or null or Nothing.

There's a good argument to be made that statements don't add anything to a language, and a language should always only be defined in terms of expressions. Existing languages that have statements are not going to get rid of them though.

 

I think the "placeholder" value you are looking for is called unit or () in most languages. Non-FP languages have it too. There it is called void but it is just a keyword, not a value you can return.

void and unit are generally type names, not values. My point was, even though some languages are syntactically defined such that everything is an expression and there is technically no statement in the language, there are still expressions that do not return any useful value. Clojure and Ruby would be examples of that.

Once static typing gets involved, the compiler may actively prevent using the return value of a void function, but that's a different story.

Here is a good post on void vs unit. Unit does in fact have a value and a type, unlike void. Otherwise I agree with you. Prime example of what you are saying in F#: when a function returns unit (which is a sign that it performs a side effect), and it contains an if statement, then the compiler will include an implicit else () if you don't supply one. I actually don't like that, because it hindered my understanding when I was first learning F#. The early things you do like printing to the console return unit, so it feels like if is a statement. But then later when you are doing other kinds of logic, it won't compile when you operate under that assumption.

code of conduct - report abuse