Subroutines and Functions
Fortran splits reusable code into two kinds of procedures: SUBROUTINEs, which perform an action and may modify their arguments but return no value directly, and FUNCTIONs, which compute and return a single value that can be used directly in an expression. Both can be internal (nested inside a PROGRAM or another procedure via CONTAINS), external (compiled separately, historically riskier because the compiler couldn't check argument consistency), or module procedures, which is now the recommended approach because it guarantees compile-time interface checking.
Cricket analogy: A team's designated 'super sub' fielder who runs on to make one specific play and comes off again is like a SUBROUTINE — invoked to perform an action (a run-out) without handing back a single number, unlike a strike rate calculation, which is more like a FUNCTION returning one value.
Defining and Calling Subroutines
A subroutine is declared with SUBROUTINE name(arg1, arg2, ...) ... END SUBROUTINE and invoked with the CALL statement, e.g., CALL update_position(x, y, dt). Arguments are passed by reference by default (with some compiler-dependent exceptions for expressions), meaning changes the subroutine makes to a dummy argument are reflected in the caller's actual argument, which is how a subroutine can 'return' multiple modified values without a single return value.
Cricket analogy: Calling CALL bowl_over(bowler_stats, over_number) hands the bowler_stats record to the subroutine by reference, so when it updates wickets and runs conceded inside the routine, the captain's scorecard back in the main program reflects those changes immediately, just like a scorer updating the master sheet in real time.
MODULE geometry
IMPLICIT NONE
CONTAINS
REAL FUNCTION circle_area(radius) RESULT(area)
REAL, INTENT(IN) :: radius
area = 3.14159265 * radius**2
END FUNCTION circle_area
SUBROUTINE scale_rectangle(width, height, factor)
REAL, INTENT(INOUT) :: width, height
REAL, INTENT(IN) :: factor
width = width * factor
height = height * factor
END SUBROUTINE scale_rectangle
END MODULE geometry
PROGRAM shapes
USE geometry
IMPLICIT NONE
REAL :: r, w, h, a
r = 4.0
a = circle_area(r)
PRINT *, 'Area = ', a
w = 10.0
h = 5.0
CALL scale_rectangle(w, h, 2.0)
PRINT *, 'Scaled: ', w, h
END PROGRAM shapesFunctions and Return Values
A function is declared with a type prefix, e.g., REAL FUNCTION compute_area(radius) ... compute_area = 3.14159 * radius**2 ... END FUNCTION, and is invoked directly within an expression, e.g., area = compute_area(5.0). Modern Fortran also allows RESULT(name) to give the return variable an explicit name distinct from the function name, which is required for recursive functions, and functions should generally be declared PURE when they have no side effects, enabling compiler optimizations and safe use inside FORALL and DO CONCURRENT constructs.
Cricket analogy: REAL FUNCTION strike_rate(runs, balls) RESULT(sr) computes and returns a single number that a commentator plugs directly into a sentence, just as sr = strike_rate(87, 52) is used inline the same way a broadcaster quotes 'a strike rate of 167' without a separate step.
Recursive functions must be declared with the RECURSIVE keyword and typically use RESULT(name) so the return variable has a distinct name from the function itself, e.g., RECURSIVE INTEGER FUNCTION factorial(n) RESULT(f) — without RECURSIVE, calling the function from within itself is a compile-time error.
Argument Passing: INTENT and Pass-by-Reference
Dummy arguments in both subroutines and functions should be declared with an INTENT attribute — INTENT(IN) for read-only inputs, INTENT(OUT) for values the procedure sets but doesn't need to read, and INTENT(INOUT) for values both read and modified — which lets the compiler catch accidental modifications of what should be read-only data and clarifies the procedure's contract for anyone reading the interface. Declaring INTENT is not just documentation: attempting to assign to an INTENT(IN) argument is a compile-time error, unlike languages where argument mutability is enforced only by convention.
Cricket analogy: Declaring pitch_report as INTENT(IN) in a subroutine that decides team selection is like a selector who reads the pitch conditions but has no authority to alter them, while the chosen_eleven argument marked INTENT(OUT) is exactly what the selector is meant to produce.
Never modify an INTENT(IN) dummy argument inside a procedure — the compiler will reject it, and relying on a function to silently mutate its inputs (instead of using INTENT(OUT/INOUT) explicitly) makes code far harder to reason about and debug in large scientific codebases.
- SUBROUTINEs perform actions and are invoked with CALL; FUNCTIONs compute and return one value usable directly in an expression.
- Arguments are passed by reference by default, so subroutines can 'return' multiple modified values through their arguments.
- Always declare INTENT(IN), INTENT(OUT), or INTENT(INOUT) on dummy arguments — the compiler enforces the contract at compile time.
- Functions can use RESULT(name) to name the return variable distinctly from the function name; this is required for RECURSIVE functions.
- PURE functions have no side effects and can be used safely inside FORALL and DO CONCURRENT constructs.
- Module procedures are preferred over external procedures because the compiler checks argument types and counts against the interface.
- Attempting to assign to an INTENT(IN) argument is a compile-time error, not just a style violation.
Practice what you learned
1. How is a Fortran SUBROUTINE invoked, as opposed to a FUNCTION?
2. What does declaring a dummy argument as INTENT(IN) guarantee?
3. What keyword is required to define a Fortran function that calls itself?
4. Why are module procedures generally preferred over external (separately compiled) procedures?
5. What makes a Fortran function eligible to be declared PURE?
Was this page helpful?
You May Also Like
Modules in Fortran
Packaging types, data, and procedures into reusable, compile-time-checked Fortran modules.
Arrays in Fortran
Declaring, indexing, sectioning, and efficiently looping over Fortran's column-major arrays.
Loops in Fortran
Fortran's DO, DO WHILE, and indefinite loop constructs, plus EXIT and CYCLE control flow.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics