Deep C Dives: First Class Functions
Written by Mike James   
Tuesday, 27 August 2024
Article Index
Deep C Dives: First Class Functions
Functions First

First class functions are what you want in an object-oriented language, but C isn't object-oriented. Even so its functions have to come first. Find out more in this extract from my latest book, Deep C Dives: Adventures in C.

Deep C Dives
Adventures in C

By Mike James

Cdive360

Buy from Amazon.

Contents

Preface
Prolog C
Dive

  1. All You Need Are Bits
  2. These aren’t the types you’re looking for
  3. Type Casting
  4. Expressions
  5. Bits and More Bits
  6. The Brilliant But Evil for ***NEW!
  7. Into the Void 
  8. Blocks, Stacks and Locals
  9. Static Storage
  10. Pointers
  11. The Array and Pointer Arithmetic
  12. Heap, The Third Memory Allocation
  13. First Class Functions
        Extract:
    First Class Functions
  14. Structs and Objects
  15. The Union
  16. Undefined Behavior
  17. Exceptions and the Long Jump

<ASIN:B0D6LZZQ8R>

Dive 13

First Class Functions

My work was fairly theoretical. It was in recursive function theory. And in particular, hierarchies of functions in terms of computational complexity.
I got involved in real computers and programming mainly by being - well, I was interested even as I came to graduate school.

Dennis Ritchie

C is a language built on functions. No, it isn’t a functional programming language, that would be a step far too far. It has functions like other languages before it had subroutines and procedures. C’s functions are designed to be a way to break a large program up into small chunks. C was just another language that emphasized “modular” programming back when it was introduced.

Today, C’s functions look a little under-powered as they lack any dynamic abilities that you will find in other current languages, but this can be seen as a strength. C’s functions are simple and efficient and adding advanced features would dilute this rare quality.

The C Function

You probably know how to create a C function, but we need to make clear what is going on at a deeper level. A function is declared and defined in the usual way:

returnType name(parameterList){functionBody}

where {functionBody} is a block of code executed when the function is called with the parameter supplied.

This is a simple pattern and one that every C programmer learns right at the very start. There are also a number of features of C functions which are obvious, but often not made explicit.

A C function can only be created at file-level and the variable that you generally regard as the function’s name is a file-level variable. That is, it is global as far as code in the same file or compilation unit goes. What is more, functions are true global variables, by default they are extern, and they can be referenced by code in other compilation units.

This is how C libraries work, they define functions which can be used by code in other files. If you want to limit the visibility of a function to its compilation unit, then mark it as static.

The fact that all functions are at file level implies that all the functions that you are going to use are defined at compile time and the lifetime of any function is the entire lifetime of the program.

If you think this is the only possibility you need to get out and meet some other languages. Modern dynamic languages – C#, Python, JavaScript, Kotlin, Rust and even Fortran 90 – allow functions to be defined within other functions. Such nested functions are local to the functions they are defined in and they can have lifetimes that depend on them being in scope. This is where some more sophisticated ideas, such as closure, come into play. If a nested function lives beyond the life of the function that contains it then it “captures” the local variables and they are available to the nested function. This is closure and exactly how the variables are made available depends on the type of closure implemented.

Other languages also allow functions to be modified by code – Python decorators, for example and this is something that was thought to be a very bad idea when C was being invented. Today, C functions cannot be modified at runtime without using machine- and compiler-specific features.

Partial Evaluation and Currying

What does C’s approach to functions make difficult?

The most obvious things that are difficult, if not impossible, are partial evaluation and currying. Partial evaluation of a function returns a function with one or more parameters fixed. For example, if you have the function sum(a,b) then a partial application is:

add1(b) = sum(1,b);

where now add1 is a function that always adds 1 to b. If you have a function of n parameters you can reduce it to a function of m parameters by applying n-m of the parameters.

Currying, a technique developed by and named after the American mathematician, Haskell Curry, is similar to partial evaluation, but it decomposes a multi-parameter function into a sequence of single parameter functions applied one after another:

sum(a,b,c) = add(a)(b)(c);

which means that sum(a) returns a function which can add b to the total and return a function which can add c to the total.

You cannot do either partial application or currying in C because you cannot create a new function at runtime, you cannot create nested functions and closures are not supported. This is a problem if you want to do functional programming in C. You also cannot implement object-oriented programming as you cannot create a bound function, i.e. a method – see Dive 14: Structs and Objects. Put simply, you cannot take a function:

myFunction(self, other parameters)

and use partial application to create a method:

myMethod(other parameters)

by setting self to reference the current instance:

myMethod = myFunction(instance, other parameters)

The key ideas are that C functions have file-level scope and are external by default, i.e. they are global variables. Functions also have a lifetime the same as the entire program and they can only be defined at compile time. Modifying a C function isn’t possible without going beyond C standards.



Last Updated ( Tuesday, 19 November 2024 )