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.janmueller.dev.
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),zeroandone.val s = Successor()with macrosuccessor.val p = projection(index)with macrosfirsttotenth.
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
ArityExceptionfor invalid amounts of arguments. - Projecting a negative index will throw a
ProjectionException. - Composing functions will throw a
CompositionExceptionif 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
OverflowExceptionif 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.
