## genequal

### Description

Compares *obj1* and *obj2* for equality using user-specified and built-in comparators.

### Author

### Requirements

None

### Generalized `equal?` predicate

*[procedure]*

`(generalized-equal? obj1 obj2 . comparator-list)`

A *comparator* is a procedure that is given two arguments to compare. It returns `#t` if its arguments are to be considered equal, `#f` if they are to be considered unequal, and any other value if it cannot decide. The third argument passed to a comparator is a list of comparators to be used in recursive calls to `generalized-equal?`.

First, each element of *comparator-list* is invoked on *obj1* and *obj2*, passing *comparator-list* as its third argument. If the comparator returns `#t` or `#f`, that is the result.

If all comparators in the list have been invoked without a `#t` or `#f` result, then `generalized-equal?` determines if both *obj1* and *obj2* are pairs, strings, vectors, u8vectors, hash tables with the same test function, or SRFI-99 records of the same type. (It cannot introspect on SRFI-9 or Chicken-native records.) If they are not, then `generalized-equal?` returns what `eqv?` returns on *obj1* and *obj2*.

Otherwise, if the containers have different numbers of elements, the result is `#f`. Otherwise, `generalized-equal?` invokes itself recursively on each corresponding element of the containers, passing itself the same comparators. If a recursive call returns `#f`, that is the result; if all recursive calls return `#t`, that is the result.

*[procedure]*

`(predicates->comparator type-predicate compare-predicate)`

Returns a comparator that invokes *type-predicate* on its first and its second arguments. If they both return `#t`, then they are assumed to be of the same type, and *compare-predicate* is invoked on the first and second arguments together. If the result is `#t` or `#f`, then the comparator returns `#t` or `#f` respectively. If they are not of the same type, a third value is returned. The comparator always ignores its third argument.

### Comparators

Specifying all three of these comparators causes `generalized-equal?` to act like Common Lisp's `EQUALP`.

*[procedure]*

`(numeric-comparator obj1 obj2 comparators-list)`

A comparator that returns `#t` if *obj1* and *obj2* are numbers that are equal by `=`, `#f` if they are not equal by `=`, and a third value otherwise. The *comparators-list* argument is ignored.

*[procedure]*

`(char-ci-comparator obj1 obj2 comparators-list)`

A comparator that returns `#t` if *obj1* and *obj2* are both characters that are equal by `char-ci=?`, `#f` if they are not equal by `char-ci=?`, and a third value otherwise. The *comparators-list* argument is ignored.

*[procedure]*

`(string-ci-comparator obj1 obj2 comparators-list)`

A comparator that returns `#t` if *obj1* and *obj2* are both strings that are equal by `string-ci=?`, `#f` if they are not equal by `string-ci=?`, and a third value otherwise. The *comparators-list* argument is ignored.

### Examples

(use genequal) (use srfi-99) (define-record-typefoo (make-foo x) foo? (x foo-x foo-set-x!)) (define-record-typebar (make-bar x) bar? (x bar-x)) (definea (make-foo 10)) (defineb (make-foo 10)) (definec (make-bar 10)) (generalized-equal? a b) => #t (generalized-equal? a c) => #f (foo-set-x! a 20) (generalized-equal? a b) => #f (generalized-equal? '("A" "B") '("a" "b")) => #f (generalized-equal? '("A" "B") '("a" "b") string-ci-comparator) => #t (generalized-equal? 2 2.0) => #f (generalized-equal? 2 2.0 numeric-comparator) => #t

### License

BSD

### Version history

#### Version 0.1

Initial release