What are first-class functions

Treat functions just like a value. Then it becomes the first-class functions. In programming a value is always first-class.

Why? For the reason that we can do anything with that value.

We can store that value in a variable, pass the value as parameter or return the value from a function.

Now think function as a value. And, as a result, we can store them in a variable, pass them to a function, store the function in a data structure. Even we can return the function from another function.

Dart programming language treats functions like any other variables. Consequently, we can use this concept in Flutter also.

In our previous section, we have built the “Play with Lexis Quiz App” using this cocept.

We have passed function as class constructor.

import 'package:flutter/material.dart';
import '../model/quiz_theme.dart';

class Answer extends StatelessWidget {
  const Answer({
    Key? key,
    required this.answer,
    required this.pointToOnPress,
  }) : super(key: key);

  final String answer;
  final VoidCallback pointToOnPress;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      margin: const EdgeInsets.all(5.0),
      child: ElevatedButton(
        onPressed: pointToOnPress,
        style: ElevatedButton.styleFrom(
          primary: QuizTheme.shrineBrown900,
        ),
        child: Text(
          answer,
          style: QuizTheme.answerStyle,
        ),
      ),
    );
  }
}

We have discussed this void callback function in our last section “What is void callback in flutter”.

And in that section we have also said that we would discuss this topic in great detail.

Now, with reference to that, we will try to understand how it works. When we treat a function as first class object, how it behaves.

Function as first class object

These terms refer to the same concept. We are treating function as a value. As a consequence, we can start with an easy example.

 void main() {
  
  int resultOfAddition = calculate(20, 10, add); 
  
  print(resultOfAddition);      // 30
  
  int resultOfSubtraction = calculate(20, 10, subtract); 
  
  print(resultOfSubtraction);   // 10
  
}

int add(int num1, int num2) {
  int result = num1 + num2;
  return result;
}

int subtract(int num1, int num2) {
  int result = num1 - num2;
  return result;
}

int calculate(int x, int y, Function performCalculation) {
  return performCalculation(x, y);
}

In the above example, we have passed function as a parameter. Consequently, we have returned the same type of function later.

Now, we can use more abstraction. We can declare a function as an instance variable. And, we can pass the variable through class constructor.

Consider this code snippet.

void main() {
  
  Robot robu = Robot(obey: bringVegetable);
  
  print(robu.obey); // Closure 'bringVegetable'
  
  robu.obey!(); // Bring some vegetable
  
  robu.obey = bringMoreRobots;
  
  robu.obey!(); // Bring some more Robots
  
}

class Robot {
  
  Function? obey;
  
  Robot({this.obey});
}

void bringVegetable() {
  print('Bring some vegetable');
}

void bringMoreRobots() {
  print('Bring some more Robots');
}

In the above example, we have used function as any type of instance variable. In addition, later, we have used the function as a normal variable.

Even we can move forward, and use any other data type as a replacement of Function data type.

During the declaration, we cannot even distinguish between whether it is a function or an instance variable of integer data type.

Look at the code below.

main(){
  
Calculator addition = Calculator(calculateNumber: calculate(10, 20, add));
  
  int adding = addition.calculateNumber!;
  print('Addition of 10 and 20 is $adding'); 
  // Addition of 10 and 20 is 30
  
  Calculator subtraction = Calculator(calculateNumber: calculate(10, 20, subtract));
  
  int subtracting = subtraction.calculateNumber!;
  print('When you subtract 20 from 10, it is $subtracting'); 
  // When you subtract 20 from 10, it is -10
  
  
}


class Calculator {
  int? calculateNumber;
  
  Calculator({this.calculateNumber});
}

int calculate(int? x, int? y, Function? performCalculation){
  return performCalculation!(x!, y!);
}

int add(int? x, int? y){
  int result = x! + y!;
  return result;
}

int subtract(int? x, int? y){
  int result = x! - y!;
  return result;
}

In the above code, initially the instance variable, or the property “calculateNumber” has not been declared as Function data type. Instead we have made it an integer data type.

When we create an object through the class constructor, we return a function of the same data type.

As we progress, we will learn more about first-class functions. Certainly, these terms are basically same.

‘A rose by any other name would smell as sweet’ is a popular adage. Because of William Shakespeare’s play Romeo and Juliet, this quote will never be haunted by thoughts of mortality.

Referring to the same quote, we can conclude that we can dub this concept any name.

The first-class function, first-class citizen, or function as first class object, whatever you call, they all mean the same concept.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

Courses at Educative

GitHub repository

Technical blog

Twitter


Posted

in

, , ,

by

Comments

Leave a Reply