{"id":13803335,"url":"https://github.com/wavebitscientific/functional-fortran","last_synced_at":"2025-05-13T15:33:13.157Z","repository":{"id":53753901,"uuid":"74594528","full_name":"wavebitscientific/functional-fortran","owner":"wavebitscientific","description":"Functional programming for modern Fortran","archived":false,"fork":false,"pushed_at":"2023-02-13T16:10:27.000Z","size":167,"stargazers_count":414,"open_issues_count":4,"forks_count":38,"subscribers_count":34,"default_branch":"master","last_synced_at":"2024-08-04T01:02:17.635Z","etag":null,"topics":["fortran","functional-programming"],"latest_commit_sha":null,"homepage":"https://wavebitscientific.github.io/functional-fortran/","language":"Fortran","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wavebitscientific.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-23T16:26:50.000Z","updated_at":"2024-07-17T12:49:15.000Z","dependencies_parsed_at":"2024-05-02T15:21:34.768Z","dependency_job_id":null,"html_url":"https://github.com/wavebitscientific/functional-fortran","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wavebitscientific%2Ffunctional-fortran","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wavebitscientific%2Ffunctional-fortran/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wavebitscientific%2Ffunctional-fortran/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wavebitscientific%2Ffunctional-fortran/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wavebitscientific","download_url":"https://codeload.github.com/wavebitscientific/functional-fortran/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225239743,"owners_count":17442822,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fortran","functional-programming"],"created_at":"2024-08-04T01:00:29.378Z","updated_at":"2024-11-18T19:31:32.599Z","avatar_url":"https://github.com/wavebitscientific.png","language":"Fortran","funding_links":[],"categories":["Functional Libraries"],"sub_categories":[],"readme":"## functional-fortran\n\nFunctional programming for modern Fortran. \n\n![Build status](https://github.com/wavebitscientific/functional-fortran/workflows/ci/badge.svg)\n[![GitHub issues](https://img.shields.io/github/issues/wavebitscientific/functional-fortran.svg)](https://github.com/wavebitscientific/functional-fortran/issues)\n\n* [Getting started](#getting-started)\n  - [Get the code](#get-the-code)\n  - [Build with fpm](#build-with-fpm)\n  - [Build with CMake](#build-with-cmake)\n  - [Or just drop-in the source file](#or-just-drop-in-the-source-file)\n  - [Use it](#use-it)\n* [Why functional-fortran?](#why-functional-fortran)\n* [What's included?](#whats-included)\n* [Example usage](#example-usage)\n* [Contributing](#contributing)\n* [Further reading](#further-reading)\n\n## Getting started\n\n### Get the code\n\n```\ngit clone https://github.com/wavebitscientific/functional-fortran\ncd functional-fortran\n```\n\n### Build with fpm\n\nThis project supports the Fortran Package Manager ([fpm](https://github.com/fortran-lang/fpm)).\n\n```\nfpm build --release\nfpm test\n```\n\nYou can also use it as a dependency in your existing fpm package.\nJust add functional-fortran to your `fpm.toml`:\n\n```toml\n[dependencies]\n[dependencies.functional]\ngit = \"https://github.com/wavebitscientific/functional-fortran\"\n```\n\n### Build with CMake\n\nAlternatively, you can build functional-fortran with CMake:\n\n```\nmkdir build\ncd build\ncmake ..\nmake\nctest\n```\n\n### Or just drop-in the source file\n\nfunctional-fortran is a single-file library.\nJust grab src/functional.f90 and build it however you want.\n\n### Use it\n\nStart using functional-fortran in your code by including the module:\n\n```\nuse functional\n```\n\n## Why functional-fortran?\n\nWhile not designed as a purely functional programming language,\nmodern Fortran goes a long way by letting the programmer\nuse `pure` functions to encourage good functional discipline, \nexpress code in mathematical form, and minimize bug-prone mutable state.\nThis library provides a set of commonly used tools in functional\nprogramming, with the purpose to help Fortran programmers\nbe less imperative and more functional.\n\n## What's included?\n\nThe following functions are provided:\n\n* `arange` returns a regularly spaced array\n* `complement` returns a set complement of two arrays\n* `empty` returns an empty array\n* `filter` filters an array using a logical input function\n* `foldl` recursively left-folds an array using an input function\n* `foldr` recursively right-folds an array using an input function\n* `foldt` recursively tree-folds an array using an input function\n* `head` returns the first element of an array\n* `init` returns everything but the last element\n* `insert` inserts an element into an array, out-of-bound safe\n* `intersection` returns a set intersection of two arrays\n* `iterfold` iteratively reduces an array using an input function\n* `last` returns the last element of an array\n* `limit` limits a scalar or array by given lower and upper bounds\n* `map` maps an array with an input function\n* `set` returns a set given input array\n* `reverse` returns array in reverse order\n* `sort` is a recursive quicksort using binary tree pivot\n* `split` returns first or second half of an array\n* `subscript` is an out-of-bound safe implementation of vector subscript\n* `tail` returns everything but the first element\n* `unfold` unfolds an array with an input function\n* `union` returns a set union of two arrays\n\nAll of the above functions are compatible with the standard Fortran 2008 kinds:\n`int8`, `int16`, `int32`, `int64`, `real32`, `real64`, `real128`,\n`complex(real32)`, `complex(real64)`, and `complex(real128)`.\n\nFurther, these functions (and their corresponding operators) \nare compatible with character strings:\n`complement`, `empty`, `head`, `init`, `intersection`, `insert`, \n`last`, `reverse`, `set`, `sort`, `split`, `tail`, and `union`.\n\nFunctions that operate on one or two arguments are also available as \nunary or binary operators, respectively. These are:\n`.complement.`, `.head.`, `.init.`, `.intersection.`, `.last.`, \n`.reverse.`, `.set.`, `.sort.`, `.tail.`, and `.union.`.\n\n## Example usage\n\n### Array functions\n\n`arange` is used to generate evenly spaced arrays,\ngiven start and end values as input arguments:\n\n```fortran\nprint *, arange(1, 5)\n           1           2           3           4           5\n```\n\n`arange` works with real numbers as well:\n\n```fortran\nprint *, arange(1., 5.)\n   1.00000000       2.00000000       3.00000000       4.00000000       5.00000000    \n```\n\nThird argument to `arange` (optional) is the increment,\nwhich defaults to `1` if not given:\n\n```fortran\nprint *, arange(1, 15, 3)\n           1           4           7          10          13\n```\n\nNegative increments work as expected:\n\n```fortran\nprint *, arange(3, 1, -1)\n           3           2           1 \n```\n\nWe can use floating-point increments:\n\n```fortran\nprint *, arange(1., 1.5, 0.1)\n   1.00000000       1.10000002       1.20000005       1.29999995       1.39999998       1.50000000    \n```\n\nIf `start` is greater than `end` and increment is positive,\n`arange` returns an empty array:\n\n```fortran\nprint *, arange(5, 1)\n\n```\n\nUse `empty` to generate a zero-length array of any Fortran standard\nkind:\n\n```fortran\nprint *, size(empty(1))\n           0\n```\nwhich may be useful to initialize accumulators, for example\nsee the implementation of set `intersection` in this library.\n\n\n`head` returns the first element of the array:\n\n```fortran\nprint *, head([1, 2, 3])\n           1\n```\n\n`tail` returns everything but the first element of the array:\n\n```fortran\nprint *, tail([1, 2, 3])\n           2           3\n```\n\nSimilarly, `last` returns the last element of the array:\n\n```fortran\nprint *, last([1, 2, 3])\n           3\n```\n\n`init` returns everything but the last element of the array:\n\n```fortran\nprint *, init([1, 2, 3])\n           1           2\n```\n\nSubscript an array at specific indices:\n\n```fortran\nprint *, subscript([1, 2, 3, 4, 5], [3, 4])\n           3           4\n```\n\nUnlike the Fortran 2008 vector subscript, the `subscript` function is out-of-bounds safe,\ni.e. subscripting out of bounds returns an empty array:\n\n```fortran\nprint *, subscript([1, 2, 3], [10])\n\n```\n\nWe can prepend, append, or insert an element into an array using `insert`:\n\n```fortran\n! insert a 5 at position 0 to prepend:\nprint *, insert(5, 0, [1, 2, 3])\n           5           1           2           3\n\n! insert a 5 at position 4 to append:\nprint *, insert(5, 4, [1, 2, 3])\n           1           2           3           5\n\n! insert a 2 at position 2:\nprint *, insert(2, 2, [1, 3, 4])\n           1           2           3           4\n```\n\n`split` can be used to return first or second half of an array:\n\n```fortran\n! return first half of the array\nprint *, split(arange(1, 5), 1)\n           1           2\n\n! return second half of the array\nprint *, split(arange(1, 5), 2)\n           3           4           5\n```\nThe above is useful for recursive binary tree searching or sorting,\nfor example, see the implementation of `sort` in this library.\n\n`sort` returns a sorted array in ascending order:\n\n```fortran\nreal :: x(5)\ncall random_number(x)\nprint *, x\n   0.997559547      0.566824675      0.965915322      0.747927666      0.367390871    \nprint *, sort(x)\n   0.367390871      0.566824675      0.747927666      0.965915322      0.997559547    \n```\nUse `reverse` to sort in descending order:\n\n```fortran\nprint *, reverse(sort(x))\n   0.997559547      0.965915322      0.747927666      0.566824675      0.367390871    \n```\n\nThe `limit` function can be used to contrain a value of a scalar\nor an array within a lower and upper limit, for example:\n\n```fortran\n! limit a scalar (5) within bounds 1 and 4\nprint *, limit(5, 1, 4)\n           4\n\n! flipping the bounds works just as well\nprint *, limit(5, 4, 1)\n           4\n```\n`limit` also works on arrays:\n\n```fortran\nprint *, limit(arange(0, 4), 1, 3):\n           1           1           2           3           3\n```\n\n### More functional: `map`, `filter`, `fold`, `unfold`\n\n`map` has the same functionality as pure elemental functions,\nbut can be used to apply recursive functions to arrays, for example:\n\n```fortran\npure recursive integer function fibonacci(n) result(fib)\n  integer,intent(in) :: n\n  if (n == 0) then\n    fib = 0\n  else if (n == 1) then\n    fib = 1\n  else\n    fib = fibonacci(n - 1) + fibonacci(n - 2)\n  end if\nend function fibonacci\n\nprint *, map(fibonacci, [17, 5, 13, 22])\n        1597           5         233       17711\n```\n\n`filter` returns array elements that satisfy a logical filtering function.\nFor example, we can define a function that returns .true. when input is an \neven number, and use this function to filter an array:\n\n```fortran\npure logical function even(x)\n  integer, intent(in) :: x\n  even = mod(x, 2) == 0\nendfunction even\n\nprint *, filter(even, [1, 2, 3, 4, 5])\n           2           4\n```\nFunctions can be chained together into pretty one-liners:\n\n```fortran\nprint *, filter(even, map(fibonacci, arange(1, 10)))\n           2           8          34\n```\n\nfunctional-fortran also provides left-, right-, and tree-fold functions,\n`foldl`, `foldr`, and `foldt`, respectively. These functions recursively\nconsume an array using a user-defined function, and return a resulting scalar.\nFor simple examples of `sum` and `product` functions using folds, we can define\nthe following addition and multiplication functions that operate on scalars:\n\n```fortran\npure real function add(x, y)\n  real, intent(in) :: x, y\n  add = x + y\nendfunction add\n\npure real function mult(x, y)\n  real, intent(in) :: x, y\n  mult = x * y\nendfunction mult\n```\nWe can then calculate the `sum` and `product` of an array by \"folding\" the \ninput using the above-defined functions and a start value \n(second argument to `fold*`):\n\n```fortran\n! left-fold an array using add to compute array sum\nprint *, foldl(add, 0., arange(1., 5.))\n   15.0000000\n\n! left-fold an array using mult to compute array product\nprint *, foldl(mult, 1., arange(1., 5.))\n   120.000000    \n```\nThe above is a trivial example that re-invents Fortran intrinsics\nas a proof of concept. Intrinsic functions should of course be used\nwhenever possible.\n\n`foldl`, `foldr`, and `foldt` return the same result if the user-defined\nfunction is associative. See the [Wikipedia page on fold](https://en.wikipedia.org/wiki/Fold_(higher-order_function)) for more information.\n`iterfold` is an iterative (non-recursive) implementation of `foldl`\nthat is provided for reference. \n\nOpposite to `fold*`, `unfold` can be used to generate an array\nbased on a start value `x`, and a function `f`, such that \nthe resulting array equals `[x, f(x), f(f(x)), f(f(f(x))), ... ]`.\nFor example:\n\n```fortran\npure real function multpt1(x)\n  real,intent(in) :: x\n  multpt1 = 1.1 * x\nendfunction multpt1\n\nwrite(*,*) unfold(multpt1, [1.], 5)\n   1.00000000       1.10000002       1.21000004       1.33100009       1.46410012 \n```\n\n### Set functions: `set`, `union`, `intersection`, `complement`\n\nFunction `set` returns all unique elements of an input array:\n\n```fortran\nprint *, set([1, 1, 2, 2, 3])\n           1           2           3\n```\nCommon functions that operate on sets, `union`, \n`intersection`, and `complement`,  are also available:\n\n```fortran\n! unique elements that are found in either array\nprint *, union([1, 2, 2], [2, 3, 3, 4])\n           1           2           3           4\n\n! unique elements that are found in both arrays\nprint *, intersection([1, 2, 2], [2, 3, 3, 4])\n           2\n\n! unique elements that are found first but not in second array\nprint *, complement([1, 2, 2], [2, 3, 3, 4])\n           1\n```\n\n## Contributing\n\nPlease submit a bug report or a request for new feature \n[here](https://github.com/wavebitscientific/functional-fortran/issues/new).\n\n## Further reading\n\n* [John Backus (1978): Can programming be liberated from the von Neumann style? A functional style and its algebra of programs](http://worrydream.com/refs/Backus-CanProgrammingBeLiberated.pdf)\n\n* [Functional programming on Wikipedia](https://en.wikipedia.org/wiki/Functional_programming)\n\n* [Fold (higher-order function) on Wikipedia](https://en.wikipedia.org/wiki/Fold_(higher-order_function))\n\n* [Graham Hutton (1999): A tutorial on the universality and expresiveness of fold](http://www.cs.nott.ac.uk/~pszgmh/fold.pdf)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwavebitscientific%2Ffunctional-fortran","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwavebitscientific%2Ffunctional-fortran","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwavebitscientific%2Ffunctional-fortran/lists"}