Skip to content

Commit

Permalink
feat: Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Olian04 committed Jul 11, 2024
1 parent f6a0575 commit b68c93d
Show file tree
Hide file tree
Showing 7 changed files with 362 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: test

on:
push:
branches:
- master
- main
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: "26.0.2"
gleam-version: "1.3.1"
rebar3-version: "3"
# elixir-version: "1.15.4"
- run: gleam deps download
- run: gleam test
- run: gleam format --check src test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.beam
*.ez
/build
erl_crash.dump
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# recursive

[![Package Version](https://img.shields.io/hexpm/v/recursive)](https://hex.pm/packages/recursive)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/recursive/)

```sh
gleam add recursive
```

```gleam
import gleam/io
import recursive
pub fn main() {
// Factorial
io.debug({
use it, f <- recursive.use_fix(10)
case it {
0 -> 1
_ -> it * f(it - 1)
}
})
let fac = recursive.fix(fn (it, f) {
case it {
0 -> 1
_ -> it * f(it - 1)
}
})
// Tail recursive factorial
io.debug({
use it, res, f <- recursive.use_fix2(10, 1)
case it {
0 -> res
_ -> f(it - 1, res * it)
}
})
let memo_fac = recursive.fix2(fn (it, res, f) {
case it {
0 -> res
_ -> f(it - 1, res * it)
}
})
// Tail recursive fibonacci
io.debug({
use it, curr, prev, f <- recursive.use_fix3(9, 1, 0)
case it {
0 -> curr
_ -> f(it - 1, curr + prev, curr)
}
})
let fib = recursive.fix3(fn (it, curr, prev, f) {
case it {
0 -> curr
_ -> f(it - 1, curr + prev, curr)
}
})
}
```

Further documentation can be found at <https://hexdocs.pm/recursive>.

## Development

```sh
gleam test
```
10 changes: 10 additions & 0 deletions gleam.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name = "recursive"
version = "1.0.0"

description = "A Fixed-point combinator implementation & utility functions for better DX when using it"
licences = ["MIT"]
repository = { type = "github", user = "olian04", repo = "gleam_recursive" }
gleam = ">= 1.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
10 changes: 10 additions & 0 deletions manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file was generated by Gleam
# You typically do not need to edit this file

packages = [
{ name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" },
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
]

[requirements]
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
132 changes: 132 additions & 0 deletions src/recursive.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/// Raw fixed-point combinator.
/// Not intended to be used standalone
pub fn rec(f) {
f(fn() { rec(f) })
}

/// Fixed-point combinator.
/// Allows anonymous functions to run recursively
/// ```gleam
/// let factorial = recursive.fix(fn (it, f) {
/// case it {
/// 0 -> 1
/// 1 -> 1
/// _ -> it * f(it - 1)
/// }
/// })
/// io.debug(factorial(10))
/// ```
pub fn fix(cb) {
rec(fn(f) { fn(a) { cb(a, cb(_, f())) } })
}

/// Two argument Fixed-point combinator.
/// Allows anonymous functions to run recursively
/// ```gleam
/// let factorial = recursive.fix2(fn (it, res, f) {
/// case it {
/// 0 -> res
/// 1 -> res
/// _ -> f(it - 1, res * it)
/// }
/// })
/// io.debug(factorial(10, 1))
/// ```
pub fn fix2(cb) {
rec(fn(f) { fn(a, b) { cb(a, b, fn(a, b) { cb(a, b, f()) }) } })
}

/// Three argument Fixed-point combinator.
/// Allows anonymous functions to run recursively
/// ```gleam
/// let fibonacci = recursive.fix3(fn (it, curr, prev, f) {
/// case it {
/// 0 -> curr
/// _ -> f(it - 1, curr + prev, curr)
/// }
/// })
/// io.debug(fibonacci(9, 1, 0))
/// ```
pub fn fix3(cb) {
rec(fn(f) { fn(a, b, c) { cb(a, b, c, fn(a, b, c) { cb(a, b, c, f()) }) } })
}

/// Four argument Fixed-point combinator.
/// Allows anonymous functions to run recursively
pub fn fix4(cb) {
rec(fn(f) {
fn(a, b, c, d) { cb(a, b, c, d, fn(a, b, c, d) { cb(a, b, c, d, f()) }) }
})
}

/// Five argument Fixed-point combinator.
/// Allows anonymous functions to run recursively
pub fn fix5(cb) {
rec(fn(f) {
fn(a, b, c, d, e) {
cb(a, b, c, d, e, fn(a, b, c, d, e) { cb(a, b, c, d, e, f()) })
}
})
}

/// Usable Fixed-point combinator.
/// Allows any block to run recursively
/// ```gleam
/// // Factorial
/// io.debug({
/// use it, f <- recursive.use_fix(10)
/// case it {
/// 0 -> 1
/// 1 -> 1
/// _ -> it * f(it - 1)
/// }
/// })
/// ```
pub fn use_fix(initial, cb) {
fix(cb)(initial)
}

/// Two argument Fixed-point combinator.
/// Allows any block to run recursively
/// ```gleam
/// // Tail recursive factorial
/// io.debug({
/// use it, res, f <- recursive.use_fix2(10, 1)
/// case it {
/// 0 -> res
/// 1 -> res
/// _ -> f(it - 1, res * it)
/// }
/// })
/// ```
pub fn use_fix2(a, b, cb) {
fix2(cb)(a, b)
}

/// Three argument Fixed-point combinator.
/// Allows any block to run recursively
/// ```gleam
/// // Fibonacci
/// io.debug({
/// use it, curr, prev, f <- recursive.use_fix3(9, 1, 0)
/// case it {
/// 0 -> curr
/// _ -> f(it - 1, curr + prev, curr)
/// }
/// })
/// ```
pub fn use_fix3(a, b, c, cb) {
fix3(cb)(a, b, c)
}

/// Four argument Fixed-point combinator.
/// Allows any block to run recursively
pub fn use_fix4(a, b, c, d, cb) {
fix4(cb)(a, b, c, d)
}

/// Five argument Fixed-point combinator.
/// Allows any block to run recursively
pub fn use_fix5(a, b, c, d, e, cb) {
fix5(cb)(a, b, c, d, e)
}
115 changes: 115 additions & 0 deletions test/recursive_test.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import gleeunit
import gleeunit/should
import recursive

pub fn main() {
gleeunit.main()
}

fn fac(it, f) {
case it {
0 -> 1
1 -> 1
_ -> it * f(it - 1)
}
}

fn memo_fac(it, res, f) {
case it {
0 -> res
1 -> res
_ -> f(it - 1, res * it)
}
}

fn fib(it, curr, prev, f) {
case it {
0 -> curr
_ -> f(it - 1, curr + prev, curr)
}
}

fn sum(a, b, c, d, f) {
case a, b, c, d {
0, 0, 0, 0 -> 0
0, 0, 0, v -> 1 + f(0, 0, 0, v - 1)
0, 0, v, _ -> 1 + f(0, 0, v - 1, d)
0, v, _, _ -> 1 + f(0, v - 1, c, d)
v, _, _, _ -> 1 + f(v - 1, b, c, d)
}
}

fn memo_sum(a, b, c, d, res, f) {
case a, b, c, d {
0, 0, 0, 0 -> res
0, 0, 0, v -> f(0, 0, 0, v - 1, res + 1)
0, 0, v, _ -> f(0, 0, v - 1, d, res + 1)
0, v, _, _ -> f(0, v - 1, c, d, res + 1)
v, _, _, _ -> f(v - 1, b, c, d, res + 1)
}
}

pub fn fix_test() {
recursive.fix(fac)(10)
|> should.equal(3_628_800)
}

pub fn fix2_test() {
recursive.fix2(memo_fac)(10, 1)
|> should.equal(3_628_800)
}

pub fn fix3_test() {
recursive.fix3(fib)(9, 1, 0)
|> should.equal(55)
}

pub fn fix4_test() {
recursive.fix4(sum)(1, 2, 3, 4)
|> should.equal(10)
}

pub fn fix5_test() {
recursive.fix5(memo_sum)(1, 2, 3, 4, 0)
|> should.equal(10)
}

pub fn use_fix_test() {
{
use it, f <- recursive.use_fix(10)
fac(it, f)
}
|> should.equal(3_628_800)
}

pub fn use_fix2_test() {
{
use it, res, f <- recursive.use_fix2(10, 1)
memo_fac(it, res, f)
}
|> should.equal(3_628_800)
}

pub fn use_fix3_test() {
{
use it, curr, prev, f <- recursive.use_fix3(9, 1, 0)
fib(it, curr, prev, f)
}
|> should.equal(55)
}

pub fn use_fix4_test() {
{
use a, b, c, d, f <- recursive.use_fix4(1, 2, 3, 4)
sum(a, b, c, d, f)
}
|> should.equal(10)
}

pub fn use_fix5_test() {
{
use a, b, c, d, res, f <- recursive.use_fix5(1, 2, 3, 4, 0)
memo_sum(a, b, c, d, res, f)
}
|> should.equal(10)
}

0 comments on commit b68c93d

Please sign in to comment.