Skip to content

Commit

Permalink
fix: make functions tail recursive
Browse files Browse the repository at this point in the history
  • Loading branch information
furrycatherder committed Apr 20, 2024
1 parent 069960f commit 8f6e2ba
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 13 deletions.
31 changes: 24 additions & 7 deletions src/ask/equivalence.gleam
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import gleam/bool
import gleam/list

pub type Equivalence(a) =
fn(a, a) -> Bool

Expand Down Expand Up @@ -30,17 +33,31 @@ pub fn not(eq: Equivalence(a)) -> Equivalence(a) {
fn(value: a, other: a) -> Bool { !eq(value, other) }
}

fn do_list(
eq: Equivalence(a),
values: List(a),
others: List(a),
acc: Bool,
) -> Bool {
case values, others {
[], [] -> acc
[value, ..values], [other, ..others] -> {
do_list(eq, values, others, acc && eq(value, other))
}
_, _ -> False
}
}

/// Create a new equivalence for a list of values based on the given equivalence.
///
pub fn list(eq: Equivalence(a)) -> Equivalence(List(a)) {
fn(values: List(a), others: List(a)) -> Bool {
case values, others {
[], [] -> True
[value, ..values], [other, ..others] -> {
eq(value, other) && list(eq)(values, others)
}
_, _ -> False
}
use <- bool.guard(
when: list.length(values) != list.length(others),
return: False,
)

do_list(eq, values, others, True)
}
}

Expand Down
20 changes: 14 additions & 6 deletions src/ask/predicate.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -35,24 +35,32 @@ pub fn not(p: Predicate(a)) -> Predicate(a) {
fn(value: a) -> Bool { !p(value) }
}

fn do_every(ps: List(Predicate(a)), value: a, acc: Bool) -> Bool {
case ps {
[] -> acc
[p, ..ps] -> do_every(ps, value, acc && p(value))
}
}

/// Combine a list of predicates together into a new predicate that returns `True`
/// if all predicates return `True`.
///
pub fn every(ps: List(Predicate(a))) -> Predicate(a) {
fn(value: a) -> Bool { do_every(ps, value, True) }
}

fn do_some(ps: List(Predicate(a)), value: a, acc: Bool) -> Bool {
case ps {
[] -> fn(_) -> Bool { True }
[p, ..ps] -> fn(value: a) -> Bool { p(value) && every(ps)(value) }
[] -> acc
[p, ..ps] -> do_some(ps, value, acc || p(value))
}
}

/// Combine a list of predicates together into a new predicate that returns `True`
/// if any predicate returns `True`.
///
pub fn some(ps: List(Predicate(a))) -> Predicate(a) {
case ps {
[] -> fn(_) -> Bool { False }
[p, ..ps] -> fn(value: a) -> Bool { p(value) || some(ps)(value) }
}
fn(value: a) -> Bool { do_some(ps, value, False) }
}

/// Create a new predicate that returns `True` if it returns `True` for every
Expand Down

0 comments on commit 8f6e2ba

Please sign in to comment.