First implementation#1
Conversation
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.
There was a problem hiding this comment.
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.
| pub fn hello_world_test() { | ||
| let name = "Joe" | ||
| let greeting = "Hello, " <> name <> "!" | ||
|
|
||
| assert greeting == "Hello, Joe!" | ||
| } |
There was a problem hiding this comment.
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.
| /// Example: | ||
| /// | ||
| /// ```gleam | ||
| /// let cmp = cmp.by(fn(u) { #(name, _) -> name }, string.compare) |
There was a problem hiding this comment.
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.
| /// let cmp = cmp.by(fn(u) { #(name, _) -> name }, string.compare) | |
| /// let cmp = cmp.by(fn(u) { case u { #(name, _) -> name } }, string.compare) |
| 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) |
There was a problem hiding this comment.
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.
| 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) |
| // 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 } |
There was a problem hiding this comment.
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.
| case s > 0.8 { True -> order.Lt False -> order.Gt } | |
| case s > 0.8 { | |
| True -> order.Lt | |
| False -> order.Gt | |
| } |
| // 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) |
There was a problem hiding this comment.
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.
| let sim = str.similarity(u.name, reference) | ||
| #(u, sim) | ||
| }) | ||
| let sorted = list.sort(with_similarity, by: cmp.by(fn(pair) { pair.1 }, float.compare)) |
There was a problem hiding this comment.
The tuple field access syntax pair.1 is not valid in standard Gleam. Tuples should be accessed using pattern matching in the key function.
| /// Example: | ||
| /// | ||
| /// ```gleam | ||
| /// let c = cmp.option(order.Lt, cmp.by_int(.age)) |
There was a problem hiding this comment.
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.
| /// 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 } })) |
| 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 }) |
There was a problem hiding this comment.
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) -> ... }.
| 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 }) |
| 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 | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| /// Reverse the ordering produced by `comp`. | ||
| /// Delegates to `order.reverse` for correctness. | ||
| pub fn reverse(comp: Comparator(a)) -> Comparator(a) { | ||
| order.reverse(comp) | ||
| } |
There was a problem hiding this comment.
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).
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 theREADME.md, a complete test suite, and configuration for CI and package publishing.The most important changes are:
Comparator Library Implementation
src/cmp.gleamwith 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
README.mdwith installation instructions, philosophy, API usage, code examples, and best practices for normalization and performance.Testing
test/cmp_gleam_test.gleamwith comprehensive tests covering all comparator functions and edge cases.Project and CI Configuration
gleam.tomlwith package metadata, dependencies, and repository links for publishing to Hex..github/workflows/test.ymlfor CI, running tests and formatting checks on pushes and pull requests.