Skip to content

A library for studying primitive recursive functions with Kotlin.

License

Notifications You must be signed in to change notification settings

DerYeger/refunk

REFUNK

License JitPack Contributions Coverage Build

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.

Installation

Gradle

Show instructions
allprojects {
  repositories {
    ...
    maven { url 'https://jitpack.io' }
  }
}
dependencies {
  implementation 'eu.yeger:refunk:{version}'
}

Maven

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>

Usage

Basic functions

  • val c = constant(value) with macros const(value), zero and one.
  • val s = Successor() with macro successor.
  • val p = projection(index) with macros first to tenth.

Composition

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

Recursion

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

Invocation

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

Additional information

More examples and various macros can be found here.

Non-recursive implementations

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.

Exceptions and error handling

  • 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.

Disclaimer

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.