Skip to content

First implementation#1

Merged
lupodevelop merged 19 commits into
mainfrom
first-implementation
Jan 8, 2026
Merged

First implementation#1
lupodevelop merged 19 commits into
mainfrom
first-implementation

Conversation

@lupodevelop

Copy link
Copy Markdown
Owner

This pull request introduces a new Gleam package, cmp_gleam, providing a small, explicit, and composable set of comparator utilities for Gleam projects. The changes include the initial implementation of the comparator library, comprehensive documentation and usage examples in the README.md, a complete test suite, and configuration for CI and package publishing.

The most important changes are:

Comparator Library Implementation

  • Added src/cmp.gleam with a suite of comparator utilities, including generic combinators (by, chain, then, lazy_then), type-specific comparators (by_int, by_string, by_float, etc.), option and list comparators, and helpers for normalization and tuple comparison.

Documentation and Examples

  • Expanded and clarified README.md with installation instructions, philosophy, API usage, code examples, and best practices for normalization and performance.

Testing

  • Added test/cmp_gleam_test.gleam with comprehensive tests covering all comparator functions and edge cases.

Project and CI Configuration

  • Added gleam.toml with package metadata, dependencies, and repository links for publishing to Hex.
  • Added .github/workflows/test.yml for CI, running tests and formatting checks on pushes and pull requests.

Add Gleam project files, a simple main module, a test file, and a GitHub Actions workflow for CI. Update README with usage, installation, and development instructions.
Introduce manifest.toml to track Gleam package dependencies and requirements, including gleam_stdlib and gleeunit.
Introduces the Order type and generic Comparator type, along with utility functions for natural integer comparison, reversing comparators, chaining comparators, and creating comparators based on integer keys.
Introduces unit tests for the cmp.natural_int and cmp.by_int functions to verify their comparison logic. These tests check ordering for integers and tuple-based key extraction.
Replaces custom Order type and comparison logic with gleam/order, gleam/int, and gleam/string modules. Adds natural_string and by_string comparators, improves type safety, and delegates ordering logic to standard library functions for correctness and maintainability.
Replaces usage of cmp.Less, cmp.Greater, and cmp.Equal with order.Lt, order.Gt, and order.Eq in tests. Adds tests for natural_string and by_string comparators to improve coverage and consistency.
Introduces the `by` function to build comparators for any type by providing a key extractor and a comparator for the key type. Also refactors imports and formatting for consistency.
Introduces by_generic_test to verify cmp.by works with both string and int comparators. Also updates imports for clarity and consistency.
Introduces `chain` and internal `chain_go` functions to allow chaining multiple comparators lexicographically. The new comparator applies each comparator in order and returns the first non-`Eq` result, or `Eq` if all comparators are equal.
Introduced natural_float and by_float for float comparison, option for comparing Option(a) values with custom order, and list_compare for lexicographic list comparison. These additions extend the comparator utilities to support more data types and use cases.
Added tests for cmp.list_compare, cmp.natural_float, cmp.by_float, and cmp.option functions to improve coverage of comparison utilities. Also refactored existing tests for readability.
Introduced `pair` and `triple` functions to compare 2- and 3-element tuples lexicographically using provided comparators. Added corresponding tests to verify correct ordering behavior for both functions.
Introduces the lazy_then function, which allows deferring the construction of a fallback comparator until it is needed. This is useful for performance optimization when the fallback comparator is expensive to build.
Introduces a new test, lazy_then_test, to verify the behavior of cmp.lazy_then when combining comparators for tuples of (name, age). The test checks correct fallback to secondary comparator and proper ordering.
Renamed the package to 'cmp_gleam' and added description, license, repository, and links fields to improve documentation and package publishing information.
Added unit tests for cmp.by_string_with and cmp.by_normalized_string functions to verify their behavior. Also renamed the test file from cmp_test.gleam to cmp_gleam_test.gleam for consistency.
Introduced by_string_with and by_normalized_string helpers for flexible string comparison, allowing custom key extraction and normalization. Also removed the unused main function and redundant io import.
Expanded the README to introduce cmp_gleam, clarify its goals, and provide detailed usage examples including sorting, comparator composition, normalization, and performance tips. Updated badges and documentation links to reference cmp_gleam instead of cmp.
Copilot AI review requested due to automatic review settings January 8, 2026 09:14
@lupodevelop lupodevelop merged commit e73428c into main Jan 8, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces cmp_gleam, a new Gleam package providing composable comparator utilities for building explicit, type-safe comparison functions. The implementation offers a clean API with combinators for chaining, mapping, and handling common data structures.

Key changes:

  • Core comparator library with generic combinators (by, chain, then, lazy_then) and type-specific helpers (by_int, by_string, by_float)
  • Comprehensive test suite covering most comparator functions
  • Complete package setup with CI configuration and documentation

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/cmp.gleam Core comparator library implementation with 20+ utility functions
test/cmp_gleam_test.gleam Test suite with 15 test functions covering primary functionality
README.md Comprehensive documentation with installation instructions, usage examples, and best practices
gleam.toml Package metadata and dependency configuration for Hex publishing
manifest.toml Generated dependency manifest with gleam_stdlib and gleeunit
.github/workflows/test.yml CI workflow for running tests and format checks

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/cmp_gleam_test.gleam
Comment on lines +14 to +19
pub fn hello_world_test() {
let name = "Joe"
let greeting = "Hello, " <> name <> "!"

assert greeting == "Hello, Joe!"
}

Copilot AI Jan 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test appears to be a placeholder/example test that doesn't test any functionality from the cmp module. It should be removed as it doesn't contribute to testing the comparator library.

Copilot uses AI. Check for mistakes.
Comment thread src/cmp.gleam
/// Example:
///
/// ```gleam
/// let cmp = cmp.by(fn(u) { #(name, _) -> name }, string.compare)

Copilot AI Jan 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code example in this comment has a syntax error. The pattern matching syntax is incorrect - it shows fn(u) { #(name, _) -> name } but should be fn(u) { case u { #(name, _) -> name } } to properly match on the tuple.

Suggested change
/// let cmp = cmp.by(fn(u) { #(name, _) -> name }, string.compare)
/// let cmp = cmp.by(fn(u) { case u { #(name, _) -> name } }, string.compare)

Copilot uses AI. Check for mistakes.
Comment thread README.md
Comment on lines +60 to +62
let cmp = cmp.chain([
cmp.by(fn(u) { case u { User(name, _) -> name } }, string.compare),
cmp.by(fn(u) { case u { User(_, age) -> age } }, cmp.natural_int)

Copilot AI Jan 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code example shows cmp.natural_int being used in a chain, but natural_int is not a comparator function - it's a function that compares two integers directly. The example should use int.compare instead, or show a different pattern.

Suggested change
let cmp = cmp.chain([
cmp.by(fn(u) { case u { User(name, _) -> name } }, string.compare),
cmp.by(fn(u) { case u { User(_, age) -> age } }, cmp.natural_int)
import gleam/int
let cmp = cmp.chain([
cmp.by(fn(u) { case u { User(name, _) -> name } }, string.compare),
cmp.by(fn(u) { case u { User(_, age) -> age } }, int.compare)

Copilot uses AI. Check for mistakes.
Comment thread README.md
// Example: sort by similarity to a reference string (careful: not a total order!)
let similarity_cmp = fn(a, b) {
let s = str.similarity(a, b)
case s > 0.8 { True -> order.Lt False -> order.Gt }

Copilot AI Jan 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code example contains a syntax error in the case expression. The condition case s > 0.8 { True -> order.Lt False -> order.Gt } is missing a semicolon or newline between the True and False branches.

Suggested change
case s > 0.8 { True -> order.Lt False -> order.Gt }
case s > 0.8 {
True -> order.Lt
False -> order.Gt
}

Copilot uses AI. Check for mistakes.
Comment thread README.md
// Compute similarity as Float, then sort by that value
let reference = "Alice"
let with_similarity = list.map(users, fn(u) {
let sim = str.similarity(u.name, reference)

Copilot AI Jan 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code example contains a syntax error. The field access syntax u.name is not valid in standard Gleam. It should use pattern matching with case expressions like the other examples in the README.

Copilot uses AI. Check for mistakes.
Comment thread README.md
let sim = str.similarity(u.name, reference)
#(u, sim)
})
let sorted = list.sort(with_similarity, by: cmp.by(fn(pair) { pair.1 }, float.compare))

Copilot AI Jan 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tuple field access syntax pair.1 is not valid in standard Gleam. Tuples should be accessed using pattern matching in the key function.

Copilot uses AI. Check for mistakes.
Comment thread src/cmp.gleam
/// Example:
///
/// ```gleam
/// let c = cmp.option(order.Lt, cmp.by_int(.age))

Copilot AI Jan 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code example in this comment has a syntax error. The field access .age is not valid Gleam syntax. It should use a case expression to extract the age field from the user record.

Suggested change
/// let c = cmp.option(order.Lt, cmp.by_int(.age))
/// let c = cmp.option(order.Lt, cmp.by_int(fn(user) { case user { User(age: age) -> age } }))

Copilot uses AI. Check for mistakes.
Comment thread README.md
Comment on lines +137 to +140
let sorted_decorated = list.sort(decorated, by: cmp.by(fn(pair) { pair.1 }, string.compare))

// Extract the original values
let sorted_users = list.map(sorted_decorated, fn(pair) { pair.0 })

Copilot AI Jan 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tuple field access syntax pair.0 and pair.1 is not valid in standard Gleam. Tuples should be accessed using pattern matching, e.g., case pair { #(first, second) -> ... }.

Suggested change
let sorted_decorated = list.sort(decorated, by: cmp.by(fn(pair) { pair.1 }, string.compare))
// Extract the original values
let sorted_users = list.map(sorted_decorated, fn(pair) { pair.0 })
let sorted_decorated = list.sort(decorated, by: cmp.by(fn(#(_user, normalized_name)) { normalized_name }, string.compare))
// Extract the original values
let sorted_users = list.map(sorted_decorated, fn(#(user, _normalized_name)) { user })

Copilot uses AI. Check for mistakes.
Comment thread src/cmp.gleam
Comment on lines +26 to +33
pub fn then(comp1: Comparator(a), comp2: Comparator(a)) -> Comparator(a) {
fn(x, y) {
case comp1(x, y) {
order.Eq -> comp2(x, y)
o -> o
}
}
}

Copilot AI Jan 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The then function is not tested. This is a core combinator that should have test coverage to verify it correctly chains comparators and only falls back to the second comparator when the first returns Eq.

Copilot uses AI. Check for mistakes.
Comment thread src/cmp.gleam
Comment on lines +18 to +22
/// Reverse the ordering produced by `comp`.
/// Delegates to `order.reverse` for correctness.
pub fn reverse(comp: Comparator(a)) -> Comparator(a) {
order.reverse(comp)
}

Copilot AI Jan 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reverse function is not tested. This important utility function should have test coverage to verify it correctly inverts the ordering of a comparator (Lt becomes Gt, Gt becomes Lt, Eq stays Eq).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants