Find out our product. Here!

Functions as Data part 1 - FP Series

Learn about first-class functions in functional programming, where functions are treated like any other data types.

In a previous posts, I talked about how functional programming treats functions as "first-class citizens." This essentially means that in functional programming, we can handle functions much like we handle other data types such as strings, numbers, or booleans.

image_title

In this post, we're going to explore what this concept of "first-class functions" means for our code and discover some practical applications.

To illustrate how first-class functions can be used similarly to other data types, consider the following example:

Let's start by defining a simple function called sayHello. All it does is print "hello" to the console when invoked. You can call this function by adding parentheses after its name, like sayHello(), and it will execute the code inside the function.

However, thanks to first-class functions, you can also define another function, let's call it sayHello2, and assign it to the sayHello function itself, like this: sayHello2 = sayHello. Now, you can call sayHello2() just like you would call the original sayHello function.

def sayHello(name):
    print(f'Hello {name}')

sayHello2 = sayHello
sayhello2('Johnny') 
  

When we run the code, it produces the same result as if we had called the original sayHello function. This same principle applies when you pass arguments to functions.

For instance, if we add a name argument to sayHello and include that name in the printed string, you can pass an argument to the new sayHello2 function, and it behaves just like calling the original function with that argument.

It's worth noting that the syntax used to assign one function to another is the same as how you'd assign other types like numbers or strings.

This means that, just like with other data types, you can do interesting things when working with functions. For example, you can use a ternary operator to dynamically change what a function does.

One practical use of this is in mocking out pieces of code during development. Suppose you have code that's time-intensive (e.g., network or database operations) or potentially destructive (e.g., deleting a database). These operations can slow down your development process.

With the concept of treating functions like other data types, you can define a "mocked-out" version of the function that returns fake data without performing the resource-intensive operations. Then, you can define an environment flag (e.g., ENVIRONMENT = 'prod') that you use during development.

ENVIRONMENT = 'prod'

def fetch_data_real():
    print('Doing some very time intensive operations...')

def fetch_data_fake():
    print('Returning fake data')
    return {
        'name': 'Jane Doe',
        'age': 34
    }

fetch_data = fetch_data_real if ENVIRONMENT == 'prod' else fetch_data_fake

data = fetch_data()
  

Based on the flag's value at runtime, you can conditionally define your function to use either the real resource-intensive version or the mock version. This approach enhances flexibility and accelerates development.

If this sounds a bit complex, don't worry! We'll break it down step by step and provide code examples to make it clearer. In essence, first-class functions empower you to handle functions more flexibly, much like you do with other data in your programs.

In a real-world scenario, this function would typically involve tasks such as fetching data from an API. However, for the sake of simplicity, we currently demonstrate its functionality by printing a message that simulates time-intensive operations. It's important to note that in practice, this is where those extensive and time-consuming operations would occur.

Moving forward, we create a placeholder function named fetch_data_fake, which serves the purpose of returning fabricated data. The intent behind this is to facilitate smoother development processes, eliminating the need to wait for the genuine, time-consuming operations of the actual function. This fabricated data is presented in the form of a dictionary containing information like names and ages.

Subsequently, we establish the primary function that will find utility in various sections of our program. In this context, we employ a ternary operator to make runtime decisions regarding the selection of either fetch_data_real or fetch_data_fake. This function is aptly named fetch_data.

Rather than adopting the conventional def keyword, we define it as fetch_data = fetch_data_real. The ternary operator enters the equation, determining that if our environment variable signifies "prod" (indicating production mode), the function will align with fetch_data_real. In any other case, it defaults to employing fetch_data_fake.

In an actual program, we would employ this function as follows: data = fetch_data. Its behavior hinges on the current environment setting, with the capability to either execute the authentic data-fetching operation or swiftly provide the synthetic data. Switching the environment variable to "prod" reverts the function to its genuine form.

This example adeptly illustrates the concept of dynamically switching functions based on runtime conditions, offering valuable versatility for tasks like A/B testing or streamlining development workflows.

Read Also :
Holla, we share any interesting view for perspective and education sharing❤️

Post a Comment

© elgharuty. All rights reserved. Developed by Jago Desain