REFUNK is small and lightweight library for studying and evaluating primitive recursive functions in Kotlin.
It provides a rich set of methods for defining functions in a more natural way than other functional frameworks and libraries.
An interactive playground is available at https://refunk.yeger.eu.
Show instructions
allprojects { repositories { ... maven { url 'https://jitpack.io' } } }
dependencies { implementation 'eu.yeger:refunk:{version}' }
Show instructions
<repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories>
<dependency> <groupId>eu.yeger</groupId> <artifactId>refunk</artifactId> <version>{version}</version> </dependency>
val c = constant(value)
with macrosconst(value)
,zero
andone
.val s = Successor()
with macrosuccessor
.val p = projection(index)
with macrosfirst
totenth
.
Function composition is handled by the Composition
class and various wrapper methods.
val f = ...
val g1 = ...
...
val gn = ...
val myComposition = f of { g1 and ... and gn }
val myAltComp = f(g1, ..., gn)
The example below demonstrates simple composition using the, in this case optional, projections first
and second
.
// f(x, y) = 2 * (x + y)
val f = multiplication(const(2), addition(first, second)) // `addition(first, second)` is equal to `addition`
val result = f(10, 11) // = 42
Unary functions can be composed with Function::andThen
.
val myComposition = myFunction andThen myUnaryFunction
Recursions can be defined using multiple extension methods.
val myRecursion = recursive(myRecursiveCaseFunction) withBaseCase myBaseCaseFunction
... = recursive {
aFunction of anotherFunction
} withBaseCase {
someFunction andThen someOtherFunction
}
Named projections help using the recursion results, parameters and arguments as well.
val addition = recursive { successor of recursionResult } withBaseCase firstBaseCaseArgument
val predecessor = recursive { recursionParameter } withBaseCase zero
The operator Function::invoke
evaluates the function for the given arguments.
val addTwo = successor andThen successor
println(addTwo(0)) //prints 2
println(addTwo(40)) //prints 42
val myFunction = predecessor of addition
println(myFunction(3, 40)) //prints 42
More examples and various macros can be found here.
REFUNK also includes non-recursive implementations of commonly used functions.
They are interchangeable with the recursive implementations and provide improved performance (and less StackOverflowErrors).
Using the non-recursive implementations of macros is highly recommended.
- Invoking a function will throw an
ArityException
for invalid amounts of arguments. - Projecting a negative index will throw a
ProjectionException
. - Composing functions will throw a
CompositionException
if the arity of the evaluating function and the number of provided functions do not match. - Invoking functions or creating constants with negative values will throw a
NaturalNumberException
. - Any provided method will throw an
OverflowException
if an overflow occurs during evaluation.
This library is intended as a tool for studying primitive recursive functions.
Because the implementation is based on experimental Kotlin features, using them in production is not recommended.