Functions and Partial Application

Introduction: Functions as Values

Welcome to the first lesson of this course! In functional programming, we view code a bit differently than in other programming styles. Our core idea, and arguably the superpower of functional programming, is that functions are treated just like regular data.

Think about how you use numbers or text in a program. You assign them to names and pass them as inputs to functions. In Haskell, you can do the exact same thing with functions themselves. You can take a function and pass it as an argument to another function.

This concept makes our code highly reusable. Instead of writing a different piece of code for every minor task, we can write a single, general structure and pass in different functions to change how it behaves. This idea sets the stage for everything we will do later in the course.

Writing a Higher-Order Function (applyTwice)

A function that takes another function as an argument, or returns a function as its result, is called a "higher-order function." Let's write our first higher-order function, called **applyTwice**. As the name suggests, this function will take an operation and apply it to a starting number twice.

First, let's look at the type signature. In Haskell, we write type signatures to explain what kind of inputs a function expects and what it will return.

Haskell
applyTwice :: (Int -> Int) -> Int -> Int

Notice the parentheses around (Int -> Int). By wrapping Int -> Int in parentheses, we are telling Haskell: "The very first argument is not a number. It is a function that takes an integer and returns an integer." Without those parentheses, a type like Int -> Int -> Int -> Int would mean a curried function that takes an Int, then another Int, then another Int, and finally returns an Int. The parentheses make the first argument itself a function.

Now, let's write the actual logic.

Haskell
applyTwice :: (Int -> Int) -> Int -> Int
applyTwice f x = f (f x)

Here, f represents our function, and x represents our starting value. In the code, we first apply f to x by writing f x. Because we want to apply it twice, we take that result and put it inside another call to f, creating f (f x).

Passing Functions as Arguments

To use applyTwice, we need a simple function to pass into it. Let's define a straightforward helper function called **increment** that just adds 1 to a number.

Haskell
increment :: Int -> Int
increment n = n + 1

Now, we can use applyTwice inside our main block. We will pass our increment function into applyTwice alongside the starting number 5.

Haskell
applyTwice :: (Int -> Int) -> Int -> Int
applyTwice f x = f (f x)

increment :: Int -> Int
increment n = n + 1

main :: IO ()
main = do
  print (applyTwice increment 5)

When we run this code, applyTwice takes increment and 5. It evaluates increment 5 to get 6, and then evaluates increment 6 to get 7.

Output:

text
7

By simply passing its name, we proved that the increment function was successfully used as an input argument.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal