Learning to Program – A Beginners Guide – Part Ten – Getting Started With Operators in F#

by Matthew Adams

Last time, we saw how to define a function in F#.

We’re going to build on that this time, so it might be a good idea to go over the key points:

1) A function takes exactly one input (parameter) and produces one output (result). We can write this as
x \mapsto f(x)

2) We can bind a function to an identifier using the let keyword.
let increment x = x + 1

3) We can define a function (with or without a binding) by using a lambda
fun x -> x + 1

4) A function is applied to the value to its immediate right
increment 3

5) A function doesn’t have to return a simple value; it can return a function too
let add x = fun y -> x + y

6) Applying 4) and 5) allows us to create functions which appear to take multiple parameters. F# has shorthand syntax to help
let add x y = x + y
add 2 3

7) We can still capture the intermediate function, effectively binding one of its parameters. We call this ‘currying’
let add2 = add 2

Finally, we left off with an exercise.

Exercise: Create a function that applies the logical XOR operator we worked out in the previous section.

Remember that we learned that the derived operator XOR can be constructed from AND, OR and NOT operators like this:

x \oplus y = (x \lor y) \land \lnot(x \land y)

You will probably also remember that the F# symbol for the logical AND operator is &&, OR is || and NOT is not. Knowing what we do about functions, we can define a function for the XOR operator.

Spot test: Define a function bound to the identifier xor which implements the XOR operator.

As usual, give it a go yourself before you look at the answer. Check it out in your F# environment.

Answer:

let xor x y = (x || y) && not (x && y)

Try that, and F# responds

val xor : x:bool -> y:bool -> bool

So, we have defined a function bound to an identifier called xor that takes a boolean, and returns a function that takes a boolean and returns a boolean – our usual pattern for a function that “takes two parameters”.

We can now make use of this to build the truth table for XOR, tidying up a loose end from our section on logic.

xor false false

val it : bool = false

xor true false

val it : bool = true

xor false true

val it : bool = true

xor true true

val it : bool = false

That’s a good start, but it doesn’t look quite right. We’re calling our xor function in the standard way: applying the function to the value to its right (or prefix syntax). But the similar operators || and && appear between the parameters, which we call infix syntax.

F# provides us with a means of defining a special kind of function called, unsurprisingly, an operator, which works in just this way.

Defining an operator in F#

Defining an operator is just like defining a function – with a couple of little wrinkles.

The first wrinkle is the name – the name has to consist of some sequence of these characters:

!%&*+-./<=>?@^ and |

The second wrinkle is to do with operator precedence. You’ll remember in the section on logic that we discussed how multiplication and division take precedence over addition and subtraction, and that logical AND takes precedence over logic OR. The precedence of a custom operator that we define is determined by the characters we use in its identifier. This can be a bit tricky to get used to!

For XOR we want a name that reminds us of the XOR symbol \oplus but which takes the same kind of precedence as OR. Let’s use |+|. It has got the pipe characters of OR, along with a plus symbol, so it looks vaguely similar.

So – how do we define an operator? As you might expect, the syntax is very similar to a function:

let ({identifier}) x y = {function body}

And here’s how we might define our XOR operator:

let (|+|) x y = (x || y) && not (x && y)

Just like a regular function binding to an identifier, except that we’re wrapping the identifier in parentheses (round brackets).

Spot test: What do you think F# will respond?
Answer: This is basically just our standard “two parameter” function pattern, so you’d expect a function that takes a boolean, and returns a function that takes a boolean and returns a boolean. And that’s just what we get. Notice that the round brackets are still shown around the identifier.

val ( |+| ) : x:bool -> y:bool -> bool

Now, though, we can try out our infix XOR operator.

true |+| false

val it : bool = true

true |+| true

val it : bool = false

So, now we know how to define functions and operators, and we’re armed with a basic knowledge of logic, we can go on to try to solve some more complex problems. But first, a couple of exercises.

Exercise 1: Another derived boolean operator is called the equivalence operator. It is true if the two operands are equal, otherwise it is false. First, draw out the truth table for the equivalence operator. Then, work out a compact boolean expression for it. Finally, implement the equivalence operator as an F# operator.

Hint: What is the relationship between the equivalence operator and the exclusive or operator?

Exercise 2: Remember the exercises in our first introduction to algorithms? Can you implement functions in F# for the sum of an arithmetic series and the sum of a geometric series?

Hint: It is probably useful to know that, in addition to + and -, F# uses / for division and * for multiplication. These are all infix operators. There is also a function called pown which is of the familiar “two parameter” prefix style, and raises one value to the power of another. Here’s 2^3 , for example:

pown 2 3

val it : int = 8

(Answers will be at the start of next week’s instalment)

Learning To Program – A Beginners Guide – Part One – Introduction
Learning To Program – A Beginners Guide – Part Two – Setting Up
Learning To Program – A Beginners Guide – Part Three – What is a computer?
Learning To Program – A Beginners Guide – Part Four – A simple model of a computer
Learning To Program – A Beginners Guide – Part Five – Running a program
Learning To Program – A Beginners Guide – Part Six – A First Look at Algorithms
Learning To Program – A Beginners Guide – Part Seven – Representing Numbers
Learning To Program – A Beginners Guide – Part Eight – Working With Logic
Learning To Program – A Beginners Guide – Part Nine – Introducing Functions
Learning To Program – A Beginners Guide – Part Ten – Getting Started With Operators in F#
Learning to Program – A Beginners Guide – Part Eleven – More With Functions and Logic in F#: Minimizing Boolean Expressions
Learning to Program – A Beginners Guide – Part Twelve – Dealing with Repetitive Tasks – Recursion in F#

Matthew Adams on Twitter