All Projects → wavebitscientific → Functional Fortran

wavebitscientific / Functional Fortran

Licence: bsd-3-clause
Functional programming for modern Fortran

Programming Languages

fortran
972 projects

Projects that are alternatives of or similar to Functional Fortran

Tofu
Functional programming toolbox
Stars: ✭ 281 (-9.65%)
Mutual labels:  functional-programming
Funcshell
Improve your shell by making it functional through Haskell! (An update to Awkward)
Stars: ✭ 297 (-4.5%)
Mutual labels:  functional-programming
Functionkit
A framework for functional types and operations designed to fit naturally into Swift.
Stars: ✭ 302 (-2.89%)
Mutual labels:  functional-programming
Eslint Plugin Functional
ESLint rules to disable mutation and promote fp in JavaScript and TypeScript.
Stars: ✭ 282 (-9.32%)
Mutual labels:  functional-programming
Hareactive
Purely functional reactive programming library
Stars: ✭ 293 (-5.79%)
Mutual labels:  functional-programming
Aecor
Pure functional event sourcing runtime
Stars: ✭ 299 (-3.86%)
Mutual labels:  functional-programming
Throwing Function
Checked Exceptions-enabled Java 8+ functional interfaces + adapters
Stars: ✭ 279 (-10.29%)
Mutual labels:  functional-programming
Shapeless
Generic programming for Scala
Stars: ✭ 3,207 (+931.19%)
Mutual labels:  functional-programming
Lambda
🔮 Estudos obscuros de programação funcional
Stars: ✭ 297 (-4.5%)
Mutual labels:  functional-programming
Awesome Idris
𝛌 Awesome Idris resources
Stars: ✭ 299 (-3.86%)
Mutual labels:  functional-programming
Javascript For Everyone
A step by step guide to learn JavaScript and programming
Stars: ✭ 285 (-8.36%)
Mutual labels:  functional-programming
Hamsters
A mini Scala utility library
Stars: ✭ 292 (-6.11%)
Mutual labels:  functional-programming
Virtual Audio Graph
🎶 Library for declaratively manipulating the Web Audio API
Stars: ✭ 299 (-3.86%)
Mutual labels:  functional-programming
Elmboy
A Nintendo™ Game Boy™ Emulator written in Elm.
Stars: ✭ 285 (-8.36%)
Mutual labels:  functional-programming
Kocircuit
Ko: A generic type-safe language for concurrent, stateful, deadlock-free systems and protocol manipulations
Stars: ✭ 305 (-1.93%)
Mutual labels:  functional-programming
Scala With Cats
Source code for Scala with Cats
Stars: ✭ 283 (-9%)
Mutual labels:  functional-programming
Android Cleanarchitecture Kotlin
This is a movies sample app in Kotlin, which is part of a serie of blog posts I have written about architecting android application using different approaches.
Stars: ✭ 3,646 (+1072.35%)
Mutual labels:  functional-programming
Quack
🐤 A multi-paradigm programming language with gradual and duck typing that targets PHP and JS
Stars: ✭ 309 (-0.64%)
Mutual labels:  functional-programming
Coconut
Simple, elegant, Pythonic functional programming.
Stars: ✭ 3,422 (+1000.32%)
Mutual labels:  functional-programming
Droste
recursion schemes for cats; to iterate is human, to recurse, divine
Stars: ✭ 300 (-3.54%)
Mutual labels:  functional-programming

functional-fortran

Functional programming for modern Fortran.

Build status GitHub issues

Getting started

Get the code

git clone https://github.com/wavebitscientific/functional-fortran
cd functional-fortran

Build with fpm

This project supports the Fortran Package Manager (fpm).

fpm build --release
fpm test

You can also use it as a dependency in your existing fpm package. Just add functional-fortran to your fpm.toml:

[dependencies]
[dependencies.functional]
git = "https://github.com/wavebitscientific/functional-fortran"

Build with CMake

Alternatively, you can build functional-fortran with CMake:

mkdir build
cd build
cmake ..
make
ctest

Or just drop-in the source file

functional-fortran is a single-file library. Just grab src/functional.f90 and build it however you want.

Use it

Start using functional-fortran in your code by including the module:

use functional

Why functional-fortran?

While not designed as a purely functional programming language, modern Fortran goes a long way by letting the programmer use pure functions to encourage good functional discipline, express code in mathematical form, and minimize bug-prone mutable state. This library provides a set of commonly used tools in functional programming, with the purpose to help Fortran programmers be less imperative and more functional.

What's included?

The following functions are provided:

  • arange returns a regularly spaced array
  • complement returns a set complement of two arrays
  • empty returns an empty array
  • filter filters an array using a logical input function
  • foldl recursively left-folds an array using an input function
  • foldr recursively right-folds an array using an input function
  • foldt recursively tree-folds an array using an input function
  • head returns the first element of an array
  • init returns everything but the last element
  • insert inserts an element into an array, out-of-bound safe
  • intersection returns a set intersection of two arrays
  • iterfold iteratively reduces an array using an input function
  • last returns the last element of an array
  • limit limits a scalar or array by given lower and upper bounds
  • map maps an array with an input function
  • set returns a set given input array
  • reverse returns array in reverse order
  • sort is a recursive quicksort using binary tree pivot
  • split returns first or second half of an array
  • subscript is an out-of-bound safe implementation of vector subscript
  • tail returns everything but the first element
  • unfold unfolds an array with an input function
  • union returns a set union of two arrays

All of the above functions are compatible with the standard Fortran 2008 kinds: int8, int16, int32, int64, real32, real64, real128, complex(real32), complex(real64), and complex(real128).

Further, these functions (and their corresponding operators) are compatible with character strings: complement, empty, head, init, intersection, insert, last, reverse, set, sort, split, tail, and union.

Functions that operate on one or two arguments are also available as unary or binary operators, respectively. These are: .complement., .head., .init., .intersection., .last., .reverse., .set., .sort., .tail., and .union..

Example usage

Array functions

arange is used to generate evenly spaced arrays, given start and end values as input arguments:

write(*,*) arange(1, 5)
           1           2           3           4           5

arange works with real numbers as well:

write(*,*) arange(1., 5.)
   1.00000000       2.00000000       3.00000000       4.00000000       5.00000000    

Third argument to arange (optional) is the increment, which defaults to 1 if not given:

write(*,*) arange(1, 15, 3)
           1           4           7          10          13

Negative increments work as expected:

write(*,*) arange(3, 1, -1)
           3           2           1 

We can use floating-point increments:

write(*,*) arange(1., 1.5, 0.1)
   1.00000000       1.10000002       1.20000005       1.29999995       1.39999998       1.50000000    

If start is greater than end and increment is positive, arange returns an empty array:

write(*,*) arange(5, 1)

Use empty to generate a zero-length array of any Fortran standard kind:

write(*,*) size(empty(1))
           0

which may be useful to initialize accumulators, for example see the implementation of set intersection in this library.

head returns the first element of the array:

write(*,*) head([1,2,3])
           1

tail returns everything but the first element of the array:

write(*,*) tail([1,2,3])
           2           3

Similarly, last returns the last element of the array:

write(*,*) last([1,2,3])
           3

init returns everything but the last element of the array:

write(*,*) init([1,2,3])
           1           2

Subscript an array at specific indices:

write(*,*) subscript([1,2,3,4,5], [3,4])
           3           4

Unlike the Fortran 2008 vector subscript, the subscript function is out-of-bounds safe, i.e. subscripting out of bounds returns an empty array:

write(*,*) subscript([1,2,3], [10])

We can prepend, append, or insert an element into an array using insert:

! insert a 5 at position 0 to prepend:
write(*,*) insert(5, 0, [1,2,3])
           5           1           2           3

! insert a 5 at position 4 to append:
write(*,*) insert(5, 4, [1,2,3])
           1           2           3           5

! insert a 2 at position 2:
write(*,*) insert(2, 2, [1,3,4])
           1           2           3           4

split can be used to return first or second half of an array:

! return first half of the array
write(*,*) split(arange(1, 5), 1)
           1           2

! return second half of the array
write(*,*) split(arange(1, 5), 2)
           3           4           5

The above is useful for recursive binary tree searching or sorting, for example, see the implementation of sort in this library.

sort returns a sorted array in ascending order:

real :: x(5)
call random_number(x)
write(*,*) x
   0.997559547      0.566824675      0.965915322      0.747927666      0.367390871    
write(*,*)sort(x)
   0.367390871      0.566824675      0.747927666      0.965915322      0.997559547    

Use reverse to sort in descending order:

write(*,*) reverse(sort(x))
   0.997559547      0.965915322      0.747927666      0.566824675      0.367390871    

The limit function can be used to contrain a value of a scalar or an array within a lower and upper limit, for example:

! limit a scalar (5) within bounds 1 and 4
write(*,*) limit(5, 1, 4)
           4

! flipping the bounds works just as well
write(*,*) limit(5, 4, 1)
           4

limit also works on arrays:

write(*,*) limit(arange(0, 4), 1, 3):
           1           1           2           3           3

More functional: map, filter, fold, unfold

map has the same functionality as pure elemental functions, but can be used to apply recursive functions to arrays, for example:

pure recursive integer function fibonacci(n) result(fib)
  integer,intent(in) :: n
  if (n == 0) then
    fib = 0
  else if (n == 1) then
    fib = 1
  else
    fib = fibonacci(n - 1) + fibonacci(n - 2)
  end if
end function fibonacci

write(*,*) map(fibonacci, [17,5,13,22])
        1597           5         233       17711

filter returns array elements that satisfy a logical filtering function. For example, we can define a function that returns .true. when input is an even number, and use this function to filter an array:

pure logical function even(x)
  integer, intent(in) :: x
  even = .false.
  if(mod(x, 2) == 0) even = .true.
endfunction even

write(*,*) filter(even,[1,2,3,4,5])
           2           4

Functions can be chained together into pretty one-liners:

write(*,*) filter(even,map(fibonacci,arange(1,10)))
           2           8          34

functional-fortran also provides left-, right-, and tree-fold functions, foldl, foldr, and foldt, respectively. These functions recursively consume an array using a user-defined function, and return a resulting scalar. For simple examples of sum and product functions using folds, we can define the following addition and multiplication functions that operate on scalars:

pure real function add(x, y)
  real, intent(in) :: x, y
  add = x + y
endfunction add

pure real function mult(x, y)
  real, intent(in) :: x, y
  mult = x * y
endfunction mult

We can then calculate the sum and product of an array by "folding" the input using the above-defined functions and a start value (second argument to fold*):

! left-fold an array using add to compute array sum
write(*,*) foldl(add,0.,arange(1.,5.))
   15.0000000

! left-fold an array using mult to compute array product
write(*,*) foldl(mult,1.,arange(1.,5.))
   120.000000    

The above is a trivial example that re-invents Fortran intrinsics as a proof of concept. Intrinsic functions should of course be used whenever possible.

foldl, foldr, and foldt return the same result if the user-defined function is associative. See the Wikipedia page on fold for more information. iterfold is an iterative (non-recursive) implementation of foldl that is provided for reference.

Opposite to fold*, unfold can be used to generate an array based on a start value x, and a function f, such that the resulting array equals [x, f(x), f(f(x)), f(f(f(x))), ... ]. For example:

pure real function multpt1(x)
  real,intent(in) :: x
  multpt1 = 1.1 * x
endfunction multpt1

write(*,*) unfold(multpt1,[1.],5)
   1.00000000       1.10000002       1.21000004       1.33100009       1.46410012 

Set functions: set, union, intersection, complement

Function set returns all unique elements of an input array:

write(*,*) set([1,1,2,2,3])
           1           2           3

Common functions that operate on sets, union, intersection, and complement, are also available:

! unique elements that are found in either array
write(*,*) union([1,2,2],[2,3,3,4])
           1           2           3           4

! unique elements that are found in both arrays
write(*,*) intersection([1,2,2],[2,3,3,4])
           2

! unique elements that are found first but not in second array
write(*,*) complement([1,2,2],[2,3,3,4])
           1

Contributing

Please submit a bug report or a request for new feature here.

Further reading

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].