Preface
Introduction
- Wo are you?
- Overview of the contents
- About the author
Fortran – An Introduction for Programmers
- Running a simple Fortran program
- Active environment: not selected?
- Configure the default compiler with an environment variable
- Programs and modules
- The
programkeyword - Repeat the name of an element in the
endstatement - The
modulekeyword - Make everything
privateby default - Aways add
onlytousestatements - Types and Variables
- Declaration comes first
- Intrinsic types
- Don’t assign a default value while declaring it
- Add
implicit none(type, external)to every module - Printing values
- Complex numbers
- Arrays
- Parameters
- Type kinds
- Functions and Subroutines
- Subroutines
- Functions
- Dummy arguments
- Declare each variable on its own line
- Pure functions
- Optional arguments
- Don’t use optional arguments
- IAdvanced Design Concepts
Object-Oriented Programming and Design
- Derived Types
- Declaring a derived type and its data components
- Naming types
- Default structure constructor
- Passing derived types as arguments
- Keep your lines short
- Encapsulation
- Type-bound procedures
- The first argument is the instance
- Type-bound and normal procedures
- Naming procedures
- Private data components
- Factory functions
- An interface for the factory functions
- An interface with the same name as the derived type
- Modeling Services as Derived Types
- The initial situation
- What are file units?
- A derived type for the logger service
- Can a user really be forced to use the factory function?
- Refactoring towards a better design
- Abstract Types and Deferred Procedures
- A deferred procedure and its interface
- An abstract factory
- Do we need two variables?
- Updating the old
log()subroutine - Polymorphism
- Service Composition and Aggregation
- The do-it-both-logger
- An array of abstract loggers?
- An array of logger references
- Delegating to any number of loggers
- Decoration
- Decorating a service abstraction
- Log levels
- Optional delegation
- Prefer to return early
- Maximum flexibility
- Module Design
- Each derived type gets its own module
- FPM naming convention
- A façade for users
- Module dependencies
- Compilation cascades and submodules
- Conclusion
Intermezzo: Enumeration
- Introduction
- A derived type for log levels
- Making the constants really constant
- Collapsing to a single data component
- Increasing type-safety
- Separating log level and log level factory
- Operator overloading
- Restoring encapsulation
- Subtypes
- Encapsulation and cohesion
- Conclusion
Functional Programming and Design
- Introduction
- Transforming lists with a filter function
- Using intrinsic function
pack - Passing a function as an argument
- Generic filtering
- Closures
- Making it generic, again
- Map
- Elemental functions
- A map function
- Mapping to other types of values
- Looking ahead
- A list type
- Reduce
- Intrinsic function
reduce - Recursion
- Pure functions
- Impure functions
- Undeterministic behavior
- Pure functions that aren’t actually pure
- Making impure functions pure
- Conclusion
Errors and Error Handling
- Introduction
- The naive way; ignoring problems
- A sensible default or alternative
- Using a boolean success return value
- Using an error return value
- Optional results
- A single, optional return value
- Preventing edge cases with types
- The Either type
- Returning either a point or an error
- Either and Option types
- Enforcing either of the data components to be provided
- Error propagation and nesting
- Nesting errors
- Guarantees; the Evidence pattern
- Language limitations
- Fatal errors
- Non-zero exit codes
- Printing an error
- Tracing the origin of an error
- Stack traces
- Conclusion
Intermezzo: A Generic to_string Function
- Introduction
- Getting started with
to_stringfunctions - A generic
to_stringfunction - Turning arrays into strings
- Derived types to string
- An abstract
to_string_ttype for derived types - Conclusion
- IITest-Driven Development and Design
Designing a test framework
- Introduction
- Temporary test programs
- Manually comparing output
- Using assertion functions
- Dealing with error margins
- Not stopping on the first failed assertion
- Printing a description of the test
- Integrating with FPM
- Unit tests and test suites
- Moving tests to their own procedures
- Moving tests to their own modules
- Generalizing the test program
- Collecting unit tests from multiple test modules
- Could the test framework discover all test modules and test procedures automatically?
- Towards a generic test runner
- Breaking out of the main program
- Making
run_test_suitestestable - Collecting more test results
- Showing progress and printing results
- Making an abstract progress printer
- Returning test and assertion errors
- Printing the test error using the progress printer
- Adding custom messages
- More assertion functions
- Introducing an interface for
assert_equals - Minimizing code duplication
- Supporting multiple kind-types
- Comparing derived types
- Improving the design: types and encapsulation
- A reusable type for “test result”
- Letting the test result handle assertion results
- Encapsulating data components of
test_result_t - Preventing “uncaught” errors
- Reporting “risky” tests
- Modularization
- Publishing the framework as a reusable FPM package
- Conclusion
TDD by Example
- Introduction
- About the test framework
- A first test case
- Considering a starting water level
- Taking into account a stop level
- Refactoring: Introduce Parameter Object
- Introducing pump state
- Keep running or stop running
- Loading configuration
- A generic data structure for configuration values
- Testing the happy path
- Adding the custom check procedure
- Testing a custom check procedure
- Implementing the configuration loader
- Checking for required values
- Type validation
- Good test names
- Conclusion
Testing Hard-to-Test Code
- Introduction
- Returning instead of printing output
- Wrapping calls to
system_clock - Using a procedure pointer
- Replacing behavior with a polymorphic service
- Conclusion
Dependency Location and Injection
- Introduction
- Initialization procedures and temporal coupling
- Splitting service locator and service factory
- Introducing a service container
- Copying by default
- Stateful services
- Sharing services as pointers
- Freeing up memory
- Service dependencies
- Passing and fetching dependencies
- Forcing services into module variables
- Turning procedures into services
- Conclusion
