Chapter 5
Booleans & Conditionals
IntroductionDecisions, decisions! To make logical decisions, programming languages need a way to determine the truthfulness of a statement. In other words, they need to be able to determine whether an expression is true or if it is false. This chapter deals with Booleans, the stuff of true and false, and the ways in which your F# programs can employ them to conditionally alter program flow. BooleansThe F# type bool maps to the .NET System.Boolean data type. It can hold one of two values: true or false. let t = true Boolean OperatorsBoolean logic enables us to logically combine true and false values to perform calculations and make decisions based on the outcome. The Boolean operators are AND, OR and NOT. Each of these operators is represented in F# as a symbolic operator as shown in Table 5.1 below.
Table 5.1 The following code snippets demonstrate Boolean values used with logical operators. let t = true // true In addition to the logical operators AND, OR and NOT, F# supports logical comparison operators. Your programs can compare for equality, values being less-than, greater-than, etc. other values, etc. Table 5.2 below summarizes the available logical comparison operators.
Logical Comparison Operators
Table 5.2 Making DecisionsOnce you can determine whether an expression is true or false, you can start using Boolean values to make decisions and alter program flow based on these decisions. The following section discusses the various ways that you can use Booleans to make decisions and conditionally execute code. if…then…elseThe if...then...else expression evaluates the truthfulness of a given Boolean expression and executes different branches of code depending on the result. The general form of the if...then…else expression is as follows: if Boolean-expression then expression1 [ else expression2 ] Note that in F#, the if...then...else construct is an expression.[1] This means that the if…then…else construct evaluates to a value, which is the value of the last expression. If there is no explicit else branch, the construct evaluates to type unit. Therefore, in the absence of an explicit else clause, the then clause must evaluate to unit. Let’s look at the consequences of this. let a, b = 100, 200 Here, we are telling F# that the expression evaluates to a string; however, this will generate an error. Why? Since there is no explicit else clause, the overall type of the if expression is unit. We are, therefore, trying to return a string where F# expects a unit. We can solve this problem by specifying an else clause that evaluates to the same type that we want to return from the other parts of the if construction. The following code shows the previous example sans the error: let a, b = 100, 200 Now, because we have an explicit else branch that returns a string, the overall expression is expected to return a string. Given that all paths evaluate to a string, F# allows the construct – with a warning. The warning in Visual Studio is: This expression should have type ‘unit’ but has type string. This warning message is telling us that the result of the expression isn’t being stored anywhere – the result is going to unit or “into the void.” To correct this, we assign the expression to an identifier: let a, b = 100, 200 Another way to correct this, or to “shut F# up” would be to tell it that we know we are explicitly ignoring the output, like this: let a, b = 100, 200 (if a < b then "a < b" else "a is not less than b") |> ignore[2] Another consequence of the if construct being an expression is that all branches must return the same data type. For example: let a, b = 100, 200 Since the else branch evaluates to a string, the overall if construct must evaluate to a string. This means that all branches must evaluate to string as well. In the example above, we are evaluating the expression to an int where F# expects a string. One more example rounds out the possibilities: let a, b = 100, 200 // OK. Since we have not
defined an else branch, F# expects this ParenthesisYou can use parenthesis to clarify or group Boolean expressions, and to force evaluation order. The following example illustrates the idea: let a, b, c = 100, 200, 300 Parentheses are optional when using them for clarity. They are mandatory for forcing evaluation order. Multiple BranchesThe if…then…else construct can evaluate multiple expressions using the else if or the elif construct. elif is a convenience keyword and is exactly the same as spelling out else if. Let’s look at a few examples: let name, pin = "bob", 123 In this example, securityMessage gets bound to the value "welcome bob!". Remember that in F# whitespace is significant. F# uses whitespace to logically and semantically group multiple lines of code together. Failure to indent or misalign the if…elif…else…then above causes compiler errors. The following example shows how F# uses indentation to group multiple statements. let name = "bob" This example prints "bob is a palindrome" and evaluates to the string "bob backwards is still bob". The final expression of each block is what the entire block evaluates to.
You may be thinking to yourself that F#’s decision-making capabilities are rather rudimentary. You may be asking, “Where’s my switch statement?” Well, it turns out that F# does not support a switch statement. Instead, it supports something much more flexible and powerful called pattern matching expressions. To take advantage of pattern matching expressions, we need a little more F# under our belts, so we’ll wait to explore them in Chapter 11. What You Need to Know· A Boolean (F# bool) is a true or false value. · F# supports the Boolean operators AND (&&), OR (||), and NOT (not). · You can test values for equality and inequality using comparison operators, e.g., =, <=, <>, etc. · You can use parenthesis to group and/or clearly delineate Boolean expressions. · The if…then…else construct evaluate to an expression. If you do not specify an explicit else clause, the entire construct is assumed to return unit. If you do specify an else clause, the construct is assumed to evaluate to the else’s return type. · When chaining multiple if expressions together, you can use elif as a convenience for “else if”. · Whitespace is significant and is used to group multiple expressions together under a given if or else clause.
[1] This is different from other .NET languages like C#, where if is a statement. [2] I know we have not yet covered this construct. It simply pipes the result of the expression to the special function ignore, which accepts anything and simply does nothing with it. We will discuss the pipe symbol |> and the ignore keyword in subsequent chapters. |
|||||||||||||||||||||||||||||||
FeedbackWe welcome your feedback. If you have comments or questions about this chapter, please feel free to e-mail us at Keep Reading |