What's a functional replacement for if-then statements?

F#Functional Programming

F# Problem Overview


I've been learning F# and functional programming and trying to do things the functional way. However, when it comes to rewriting some code I'd already written in C# I get stuck at simple if-then statements (ones that only do something, not return a value). I know you can pull this off in F#:

if expr then do ()

However, I thought this was an imperative approach to coding? Maybe I've not learned enough about functional programming, but it doesn't seem functional to me. I thought the functional approach was to compose functions and expressions, not simply execute statements one after the other which is what if-then seems to encourage.

So, am I missing something and if-then is perfectly fine in the functional world? If not, what is the functional equivalent of such a statement? How could I take an if-then and turn it functional?

Edit: I might've asked the wrong question (sorry, still fairly new to functional programming): Let's take a real world example that made me even ask this:

if not <| System.String.IsNullOrWhiteSpace(data) then do
    let byteData = System.Text.Encoding.Unicode.GetBytes(data)
    req.ContentLength <- int64 byteData.Length
    let postStream : System.IO.Stream = req.GetRequestStream()
    postStream.Write(byteData, 0, byteData.Length)
    postStream.Flush()
    postStream.Dispose()

The body of that if-then doesn't return anything, but I don't know how I could make this more functional (if that's even possible). I don't know the proper technique for minimizing imperative code. Given F#'s nature it's fairly easy to just transport my C# directly, but I'm having difficulties turning it functional. Every time I reach such an if statement in C#, and I'm trying to transport it to F#, I get discouraged that I can't think of a way to make the code more functional.

F# Solutions


Solution 1 - F#

An important point that hasn't been mentioned so far is the difference between if .. then .. else and if .. then without the else branch.

If in functional languages

The functional interpretation of if is that it is an expression that evaluates to some value. To evaluate the value of if c then e1 else e2 you evaluate the condition c and then evaluate either e1 or e2, depending on the condition. This gives you the result of the if .. then .. else.

If you have just if c then e, then you don't know what the result of the evaluation should be if c is false, because there is no else branch! The following clearly does not make sense:

let num = if input > 0 then 10

In F#, expressions that have side-effects like printf "hi" return a special value of type unit. The type has only a single value (written as ()) and so you can write if which does an effect in just a single case:

let u = if input > 0 then printf "hi" else ()

This always evaluates to unit, but in the true branch, it also performs the side-effect. In the false branch, it just returns a unit value. In F#, you don't have to write the else () bit by hand, but conceptually, it is still there. You can write:

let u = if input > 0 then printfn "hi"
Regarding your additional example

The code looks perfectly fine to me. When you have to deal with API that is imperative (like lots of the .NET libraries), then the best option is to use the imperative features like if with a unit-returning branch.

You can use various tweaks, like represent your data using option<string> (instead of just string with null or empty string). That way, you can use None to represent missing data and anything else would be valid input. Then you can use some higher-order functions for working with options, such as Option.iter, which calls a given function if there is a value:

maybeData |> Option.iter (fun data ->
    let byteData = System.Text.Encoding.Unicode.GetBytes(data)  
    req.ContentLength <- int64 byteData.Length  
    use postStream = req.GetRequestStream()  
    postStream.Write(byteData, 0, byteData.Length) )

This is not really less imperative, but it is more declarative, because you don't have to write the if yourself. BTW: I also recommend using use if you want to Dispose object auotmatically.

Solution 2 - F#

There's nothing wrong with if-then in functional world.

Your example is actually similar to let _ = expr since expr has side effects and we ignore its return value. A more interesting example is:

if cond then expr

which is equivalent to:

match cond with
| true -> expr
| false -> ()

if we use pattern matching.

When the condition is simple or there is only one conditional expression, if-then is more readable than pattern matching. Moreover, it is worth to note that everything in functional programming is expression. So if cond then expr is actually the shortcut of if cond then expr else ().

If-then itself is not imperative, using if-then as a statement is an imperative way of thinking. From my experience, functional programming is more about the way of thinking than concrete control flows in programming languages.

EDIT:

Your code is totally readable. Some minor points are getting rid of redundant do keyword, type annotation and postStream.Dispose() (by using use keyword):

if not <| System.String.IsNullOrWhiteSpace(data) then
    let byteData = System.Text.Encoding.Unicode.GetBytes(data)
    req.ContentLength <- int64 byteData.Length
    use postStream = req.GetRequestStream()
    postStream.Write(byteData, 0, byteData.Length)
    postStream.Flush()

Solution 3 - F#

It's not the if-expression that's imperative, it's what goes in the if-expression. For example, let abs num = if num < 0 then -num else num is a totally functional way to write the abs function. It takes an argument and returns a transformation of that argument with no side effects. But when you have "code that only does something, not return a value," then you're writing something that isn't purely functional. The goal of functional programming is to minimize the part of your program that can be described that way. How you write your conditionals is tangential.

Solution 4 - F#

In order to write complex code, you need to branch at some point. There's a very limited number of ways you can do that, and all of them require a logical flow through a section of code. If you want to avoid using if/then/else it's possible to cheat with loop/while/repeat - but that will make your code far less reasonable to maintain and read.

Functional programming doesn't mean you shouldn't execute statements one after the other - it simply means that you shouldn't have a mutable state. Each function needs to reliably behave the same way each time it is called. Any differences in how data is handled by it need to be accounted for by the data that is passed in, instead of some trigger that is hidden from whatever is calling the function.

For example, if we have a function foo(int, bool) that returns something different depending on whether bool is true or false, there will almost certainly be an if statement somewhere in foo(). That's perfectly legitimate. What is NOT legitimate is to have a function foo(int) that returns something different depending on whether or not it is the first time it is called in the program. That is a 'stateful' function and it makes life difficult for anyone maintaining the program.

Solution 5 - F#

It's considered functional if your if statement has a return value and does not have side effects.

Suppose you wanted to write the equivalent of:

if(x > 3) n = 3; else n = x;

Instead of doing that, you use the return statement from the if command:

let n = (if x > 3 then 3 else x)

This hypothetical if is suddenly functional because it has no side effects; it only returns a value. Think of it as if it were the ternary operator in some languages: int n = x>3?3:x;

Solution 6 - F#

There are two observations that can assist in the transition from imperative to functional ("everything is an expression") programming:

  1. unit is a value, whereas an expression returning void in C# is treated as a statement. That is, C# makes a distinction between statements and expressions. In F# everything is an expression.

  2. In C# values can be ignored; in F# they cannot, therefore providing a higher level of type safety. This explicitness makes F# programs easier to reason about and provides greater guarantees.

Solution 7 - F#

My apologies for not knowing F#, but here is one possible solution in javascript:

function $if(param) {
	return new Condition(param)
}

function Condition(IF, THEN, ELSE) {
	this.call = function(seq) {
		if(this.lastCond != undefined) 
			return this.lastCond.call(
				sequence(
					this.if, 
					this.then, 
					this.else, 
					(this.elsif ? this.elsif.if : undefined),
					seq || undefined
				)
			);
		 else 
			return sequence(
				this.if, 
				this.then, 
				this.else, 
				(this.elsif ? this.elsif.if : undefined),
				seq || undefined
			)
	}
		

	this.if   = IF ? IF : f => { this.if = f; return this };
    this.then = THEN ? THEN : f => { this.then = f; return this };
    this.else = ELSE ? ELSE : f => { this.else = f; return this };
	this.elsif = f => {this.elsif = $if(f); this.elsif.lastCond = this; return this.elsif};
}

function sequence(IF, THEN, ELSE, ELSIF, FINALLY) {
	return function(val) {
		if( IF(val) ) 
			return THEN();

		else if( ELSIF && ELSIF(val) ) 
			return FINALLY(val);

		else if( ELSE ) 
			return ELSE();

		else 
			return undefined
		
	}
}}

The $if function returns an object with the if..then..else..elsif construct, using the Condition constructor. Once you call Condition.elsif() you'll create another Condition object - essentially creating a linked list that can be traversed recursively using sequence()

You could use it like:

var eq = val => x => x == val ? true : false;

$if( eq(128) ).then( doStuff ).else( doStuff )
.elsif( eq(255) ).then( doStuff ).else( doStuff ).call();

However, I realize that using an object is not a purely functional approach. So, in that case you could forgo the object all together:

sequence(f, f, f, f,
    sequence(f, f, f, f
        sequence(f, f, f)
    )
);

You can see that the magic is really in the sequence() function. I won't try to answer your specific question in javascript. But I think the main point is that you should make a function that will run arbitrary functions under an if..then statement, then nest multiples of that function using recursion to create a complex logical chain. At least this way you will not have to repeat yourself ;)

This code is just a prototype so please take it as a proof of concept and let me know if you find any bugs.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBobView Question on Stackoverflow
Solution 1 - F#Tomas PetricekView Answer on Stackoverflow
Solution 2 - F#padView Answer on Stackoverflow
Solution 3 - F#ChuckView Answer on Stackoverflow
Solution 4 - F#Michael MartinView Answer on Stackoverflow
Solution 5 - F#TimView Answer on Stackoverflow
Solution 6 - F#DanielView Answer on Stackoverflow
Solution 7 - F#DucoView Answer on Stackoverflow