Functions in Fortran
Learn how to define functions and subroutines in Fortran, including arguments, INTENT attributes, recursion, optional arguments, and passing procedures as parameters
Functions are how Fortran organizes reusable computation, and they are central to the language’s identity — the name Fortran itself comes from “FORmula TRANslation.” From the earliest versions, the ability to write a mathematical formula once and call it repeatedly was the whole point.
Fortran makes a clear distinction that many modern languages blur: it has both functions and subroutines. A function takes inputs and returns a single value, so you use it inside an expression (y = square(x)). A subroutine performs an action and communicates results back through its arguments, so you invoke it with call. Knowing which one to reach for is the first thing to learn about procedures in Fortran.
As an imperative, statically-typed language, Fortran requires you to declare the type of every argument and every result. Modern Fortran organizes procedures inside modules, which give them explicit interfaces — the compiler can then check that every call passes the right number and type of arguments. In this tutorial you’ll learn how to define functions and subroutines, control how arguments flow with intent, write recursive procedures, use optional and keyword arguments, and even pass one procedure to another.
Functions vs. Subroutines
A function returns a value; a subroutine performs an action. The result clause names the variable that holds a function’s return value, and contains groups the procedures inside a module.
Create a file named functions.f90:
| |
A function is used inside an expression (area = circle_area(2.0)), while a subroutine is invoked with the call statement (call print_banner(...)). The character(len=*) declaration is an assumed-length string — the subroutine accepts a string argument of any length.
Arguments and the INTENT Attribute
The intent attribute documents how each argument is used and lets the compiler catch mistakes. intent(in) is read-only, intent(out) is a result the procedure must set, and intent(inout) is modified in place. Because subroutines can have several intent(out) arguments, they are the idiomatic way to return more than one value.
Create a file named intent_args.f90:
| |
The I0 edit descriptor prints an integer using the minimum width needed, which keeps formatted output tidy. Declaring intent is optional, but it is a best practice in modern Fortran: if you accidentally assign to an intent(in) argument, the compiler refuses to build.
Recursion
A procedure that calls itself must be marked with the recursive keyword, and a recursive function must use a result clause. Variables declared inside a procedure are local by default — each recursive call gets its own copy, which is exactly what recursion needs.
Create a file named recursion.f90:
| |
The Fibonacci line uses an implied-do loop (fib(i), i = 0, 9) inside the output list together with the repeating format 10(I0, 1X) — a compact way to print a whole sequence on one line.
Optional and Keyword Arguments
Fortran supports optional arguments via the optional attribute, and the built-in present() function tests whether the caller supplied one. This is how Fortran provides default-like behavior. You can also use keyword arguments to name parameters at the call site, which improves readability and lets you skip optional arguments in the middle of a list.
Create a file named optional_args.f90:
| |
The // operator concatenates strings. Notice that present(greeting) is the only safe way to know whether an optional argument was passed — reading an absent optional argument is undefined behavior.
Passing Procedures as Arguments
Fortran lets you pass one procedure to another. The receiving procedure declares the expected signature with an interface block, so the compiler still type-checks every call. This enables higher-order patterns like applying a chosen operation to a pair of values.
Create a file named higher_order.f90:
| |
Because add and multiply are module procedures, they already have explicit interfaces, so they can be passed by name as actual arguments to apply.
Running with Docker
You can compile and run every example with the official GCC image, which includes gfortran — no local install required.
| |
Expected Output
Running functions.f90:
===============
Circle Area
===============
Area of radius 2.0: 12.5664
Running intent_args.f90:
Before swap: x = 10, y = 20
After swap: x = 20, y = 10
17 / 5 = 3 remainder 2
Running recursion.f90:
Factorials:
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
Fibonacci sequence:
0 1 1 2 3 5 8 13 21 34
Running optional_args.f90:
Hello, Ada!
Welcome, Grace!
Greetings, Alan!
Running higher_order.f90:
add(3, 4) = 7.00
multiply(3, 4) = 12.00
Key Concepts
- Functions vs. subroutines — A
functionreturns a single value and is used in expressions; asubroutineperforms an action and is invoked withcall. - The
resultclause — Names the variable that carries a function’s return value; it is required for recursive functions. intentattributes —intent(in),intent(out), andintent(inout)document and enforce how each argument is used, letting the compiler catch misuse.- Multiple return values — Use a subroutine with several
intent(out)arguments, since functions return only one value. - Recursion — Mark self-calling procedures with
recursive; local variables give each call its own private state. - Optional and keyword arguments — The
optionalattribute pluspresent()provide default-like behavior, and keyword arguments make calls clearer. - Modules give explicit interfaces — Defining procedures inside a
modulewithcontainslets the compiler type-check every call automatically. - Procedures as arguments — An
interfaceblock lets you pass one procedure to another for higher-order patterns.
Comments
Loading comments...
Leave a Comment