On this page:
4.1 Interfaces
ID
reify
4.1.1 Concatenation
gen:  appendable
appendable?
append
appendable-identity
appendable-inverse
4.1.2 Multiplication
gen:  multipliable
multipliable?
multiply
multipliable-identity
multipliable-inverse
4.1.3 Addition
gen:  addable
addable?
add
addable-identity
addable-inverse
4.2 Utilities
..
*
+
id
inverse
-
/
fold
foldl
foldr
fold/  steps
foldl/  steps
foldr/  steps
sum
product

4 Algebraic Operators

 (require relation/algebraic) package: Relation

Generic algebraic operators for composing data.

The built-in operators + and * operate on numbers specifically. Often, however, we are interested in performing operations "similar" to these for datatypes that aren’t numbers, for which we would resort to type-specific operators like append for lists.

This module generalizes the standard algebraic operators to work on any type that supports a "canonical" notion of addition, multiplication, or concatenation. This allows our intuitions about addition and other forms of composition to extend over all appropriate types via the use of the common generic operators +, * and ...

4.1 Interfaces

This module provides three generic interfaces – gen:appendable, gen:multipliable, and gen:addable. These are meant to represent the canonical "idea" of the operations of concatenation, multiplication and addition, respectively, whose behavior may be customized for each type via these generic interfaces, and used via the common operators .. (concatenation), * (multiplication), and + (addition).

In order to support generic composition seamlessly, all of the composition interfaces support a generic (rather than type- and operation-specific) identity value that is employed in cases where type information is not available.

value

ID : composition-identity?

The special value ID serves as the generic identity value for all composition operations when the type of the operands is not known. It may appear at intermediate stages of a computation when there isn’t sufficient information to infer a type-specific identity. Any value when composed with ID yields itself.

Examples:
> (+ 5 ID)

5

> (* ID 5)

5

> (+)

(composition-identity)

> (apply * '())

(composition-identity)

In the event no operands are received in the course of a computation, the result of composition would be ID, which would not be a usable result in utilities that are expecting a specific type such as a string. In such cases, the result could be converted to the expected type using one of the transformers in relation/transform such as ->string. If you are not using a built-in type but rather a custom type, however, you could use the following more general utility to "reify" the generic identity value to a type of your choosing:

procedure

(reify v example)  any/c

  v : any/c
  example : any/c
"Reifies" a value to a specific type. If the value is already a tangible value (i.e. anything other than ID), then it is returned without modification. Otherwise, the appropriate nullary value for the type is returned. The nullary value is defined as the identity value for the append operation for the type, so custom types are expected to implement the gen:appendable interface in order to leverage this utility.

Examples:
> (reify ID 3)

0

> (reify ID "cherry")

""

> (reify ID (list))

'()

> (reify "hi" (list))

"hi"

> (reify '(1 2 3) "")

'(1 2 3)

4.1.1 Concatenation

A generic interface that represents any object for which a concatenation or "append-like" operation can be defined. The following built-in types have implementations for gen:appendable:

Examples:
> (.. "hi" " " "there")

"hi there"

> (.. '(1 2 3) '(4 5 6))

'(1 2 3 4 5 6)

procedure

(appendable? v)  boolean?

  v : any/c
Predicate to check if a value may be operated on using the generic append operator, .. or .

Examples:
> (appendable? 3)

#t

> (appendable? #\a)

#f

> (appendable? "cherry")

#t

> (appendable? (list))

#t

> (appendable? (set))

#t

> (appendable? (hash))

#t

> (appendable? (vector))

#t

To implement this interface for custom types, the following methods need to be implemented, unless the type already implements another interface for which a default implementation exists for gen:appendable (such as gen:sequence) and if more specific handling is not needed for the custom type.

procedure

(append a b)  appendable?

  a : appendable?
  b : appendable?
A function taking two arguments that composes them in the natural "append-like" operation for the type. Both arguments are expected to be instances of the structure type to which the generic interface is associated (or a subtype of the structure type).

In addition to providing a definition of concatenation appropriate to the type, implementations of this method must also handle the special value ID in the following way: if the operand b is eq? to ID, then the result of the function must be a.

procedure

(appendable-identity a)  appendable?

  a : appendable?
A function that returns the identity value for the type, for the append operation. The identity value is that value which, when composed with other values of the same type under the append operation, yield those other values as if there had been no composition. The identity value is expected to be an instance of the structure type to which the generic interface is associated (or a subtype of the structure type).

procedure

(appendable-inverse a)  appendable?

  a : appendable?
A function that returns the inverse value for the type, for the append operation. The inverse value is that value which, when composed with other values of the same type under the append operation, yields the identity value. The inverse value is expected to be an instance of the structure type to which the generic interface is associated (or a subtype of the structure type).

Providing an implementation for this method is optional, and most types would usually not define an inverse for an append-like operation. That is, in mathematical terms, append-like operations typically form an algebraic semigroup or monoid rather than a group.

4.1.2 Multiplication

A generic interface that represents any object for which a "multiplication-like" operation can be defined. The following built-in types have implementations for gen:multipliable:

Since numbers are the only common type for which the multiply operation is defined, this interface is present mostly for uniformity, being leveraged by utilities like fold, and of course also for use in custom types.

Example:
> (* 1 2 3 4)

24

procedure

(multipliable? v)  boolean?

  v : any/c
Predicate to check if a value may be operated on using the generic multiplication operator, *.

Examples:
> (multipliable? 3)

#t

> (multipliable? #\a)

#f

> (multipliable? "cherry")

#f

> (multipliable? (list))

#f

To implement this interface for custom types, the following methods need to be implemented:

procedure

(multiply a b)  multipliable?

  a : multipliable?
  b : multipliable?
A function taking two arguments that composes them in the natural "multiplication-like" operation for the type. Both arguments are expected to be instances of the structure type to which the generic interface is associated (or a subtype of the structure type).

In addition to providing a definition of multiplication appropriate to the type, implementations of this method must also handle the special value ID in the following way: if the operand b is eq? to ID, then the result of the function must be a.

A function that returns the identity value for the type, for the multiply operation. The identity value is that value which, when composed with other values of the same type under the multiply operation, yield those other values as if there had been no composition. The identity value is expected to be an instance of the structure type to which the generic interface is associated (or a subtype of the structure type).

procedure

(multipliable-inverse a)  multipliable?

  a : multipliable?
A function that returns the inverse value for the type, for the multiply operation. The inverse value is that value which, when composed with other values of the same type under the multiply operation, yields the identity value. The inverse value is expected to be an instance of the structure type to which the generic interface is associated (or a subtype of the structure type).

Providing an implementation for this method is optional; a multiply operation need not admit an inverse, for instance, for a custom integer type, multiplication would not define an inverse since multiplicative inverses for numbers would be rational numbers, rather than integers, thus being excluded by the "same type" requirement above.

4.1.3 Addition

A generic interface that represents any object for which an "addition-like" (group) operation can be defined. The following built-in types have implementations for gen:addable:

Examples:
> (+ 1 2 3)

6

> (+ #(1 2 3) #(1 2 3) #(1 2 3))

'#(3 6 9)

procedure

(addable? v)  boolean?

  v : any/c
Predicate to check if a value may be operated on using the generic addition operator, +.

Examples:
> (addable? 3)

#t

> (addable? #\a)

#f

> (addable? "cherry")

#f

> (addable? (list))

#f

> (addable? (set))

#f

> (addable? (hash))

#f

> (addable? (vector))

#t

To implement this interface for custom types, the following methods need to be implemented:

procedure

(add a b)  addable?

  a : addable?
  b : addable?
A function taking two arguments that composes them in the natural "addition-like" operation for the type. Both arguments are expected to be instances of the structure type to which the generic interface is associated (or a subtype of the structure type).

In addition to providing a definition of addition appropriate to the type, implementations of this method must also handle the special value ID in the following way: if the operand b is eq? to ID, then the result of the function must be a.

procedure

(addable-identity a)  addable?

  a : addable?
A function that returns the identity value for the type, for the add operation. The identity value is that value which, when composed with other values of the same type under the add operation, yield those other values as if there had been no composition. The identity value is expected to be an instance of the structure type to which the generic interface is associated (or a subtype of the structure type).

procedure

(addable-inverse a)  addable?

  a : addable?
A function that returns the inverse value for the type, for the add operation. The inverse value is that value which, when composed with other values of the same type under the add operation, yields the identity value. The inverse value is expected to be an instance of the structure type to which the generic interface is associated (or a subtype of the structure type).

Providing an implementation for this method is required; an addition operation must admit an inverse. That is, in mathematical terms, addition-like operations are expected to form an algebraic group.

4.2 Utilities

procedure

(.. v ...)  appendable?

  v : appendable?

procedure

( v ...)  appendable?

  v : appendable?
Performs the canonical "append-like" operation on the data based on its type, taking an arbitrary number of arguments. The special value ID serves as the generic identity value for all composition operations when the type of the operands is not known. In particular, this value is the result when no operands are provided.

Examples:
> (.. "hi" " " "there")

"hi there"

> (.. '(1 2 3) '(4 5 6))

'(1 2 3 4 5 6)

> (.. (hash 'a 1 'b 2) (hash 'c 3))

'#hash((a . 1) (c . 3) (b . 2))

> ((.. ->string +) 3 4)

"7"

> ( "hi" " " "there")

"hi there"

procedure

(* v ...)  multipliable?

  v : multipliable?
Performs the canonical "multiplication-like" operation on the data based on its type, taking an arbitrary number of arguments. The special value ID serves as the generic identity value for all composition operations when the type of the operands is not known. In particular, this value is the result when no operands are provided.

Example:
> (* 1 2 3 4)

24

procedure

(+ v ...)  addable?

  v : addable?
Performs the canonical "addition-like" operation on the data based on its type, taking an arbitrary number of arguments. The special value ID serves as the generic identity value for all composition operations when the type of the operands is not known. In particular, this value is the result when no operands are provided.

Examples:
> (+ 1 2 3 4)

10

> (+ #(1 2 3) #(1 2 3) #(1 2 3))

'#(3 6 9)

procedure

(id operation)  procedure?

  operation : procedure?
Produces the "identity" procedure for the given canonical operation, which, when evaluated for a particular value, yields the identity value for that type under the indicated operation.

Examples:
> ((id add) 3)

0

> ((id multiply) 3)

1

> ((id add) #(1 2 -3))

'#(0 0 0)

> ((id append) "hi")

""

> ((id append) '(1 2 3))

'()

> ((id ..) "hi")

""

> ((id +) 3)

0

> ((id *) 3)

1

procedure

(inverse operation)  procedure?

  operation : procedure?
Produces the "inverse" procedure for the given canonical operation, which, when evaluated for a particular value, yields the inverse value for that type under the indicated operation.

Examples:
> ((inverse +) 3)

-3

> ((inverse *) 3)

1/3

> ((inverse +) #(1 2 -3))

'#(-1 -2 3)

procedure

(- v ...)  addable?

  v : addable?
A general version of "subtraction" that works no differently than usual on numbers, but also supports any other group type, for instance, vectors. The result is computed by adding the first supplied value to the inverse of every subsequent value. If only one argument is provided, then it simply returns the additive inverse.

Examples:
> (- 5 3)

2

> (- #(3 3 3) #(0 1 0) #(0 0 2))

'#(3 2 1)

> (- 5)

-5

procedure

(/ v ...)  multipliable?

  v : multipliable?
A general version of "division" that works no differently than usual on numbers, but also supports any other multipliable type. The result is computed by multiplying the first supplied value with the inverse of every subsequent value. If only one argument is provided, then it simply returns the multiplicative inverse.

Examples:
> (/ 5 3)

5/3

> (/ 5)

1/5

procedure

(fold f    
  seqs    
  [#:into base    
  #:order order    
  #:direction direction    
  #:with-steps? with-steps?])  any/c
  f : procedure?
  seqs : (listof (sequenceof any/c))
  base : any/c = #f
  order : (one-of/c 'abb 'bab) = 'abb
  direction : (one-of/c 'left 'right) = 'right
  with-steps? : boolean? = #f

procedure

(foldl f    
  seqs    
  [#:into base    
  #:order order    
  #:with-steps? with-steps?])  any/c
  f : procedure?
  seqs : (listof (sequenceof any/c))
  base : any/c = #f
  order : (one-of/c 'abb 'bab) = 'abb
  with-steps? : boolean? = #f

procedure

(foldr f    
  seqs    
  [#:into base    
  #:order order    
  #:with-steps? with-steps?])  any/c
  f : procedure?
  seqs : (listof (sequenceof any/c))
  base : any/c = #f
  order : (one-of/c 'abb 'bab) = 'abb
  with-steps? : boolean? = #f
Similar to foldl and foldr, but infers the relevant identity element where possible and uses it as the base value, if none is provided. The identity element is determined by considering the first element of the input sequence seqs (or of the first input sequence, if multiple sequences are provided) together with the given operation f.

With folding operations there are two parameters that one may wish to tweak. The first is the direction of the fold, either left or right, for which one may use either foldl or foldr. The second is the order in which arguments are supplied to the folding function f, which may be controlled by the keyword argument #:order, with a value of 'abb corresponding to the accumulator always being passed last, consistent with Racket’s built-in foldl, and a value of 'bab corresponding to the accumulator always being passed first, consistent with the version of foldl found in data/collection and also in some other functional languages like Haskell.

In many common cases, modulating the folding direction and/or the argument order does not make a difference to the result. Specifically, in those cases where the operation is commutative and closed, it doesn’t matter whether you use foldl or foldr, or whether you use argument order 'abb or 'bab. The result would be the same. However, in cases where the operation is not closed, argument order becomes significant. As a general guideline, choose between foldl and foldr in cases where the operation is not commutative (a relatively common case, such as string concatenation), and between the two argument orders in cases where the operation isn’t closed (a less common case, such as type constructors).

foldl is equivalent to calling fold with #:direction 'left, and foldr is equivalent to calling fold with #:direction 'right. fold/steps is equivalent to calling fold with #:with-steps? #t.

Examples:
> (fold + '(1 2 3 4))

10

> (fold * '(1 2 3 4))

24

> (fold .. '("hi" " " "there"))

"hi there"

> (foldr + '(1 2 3 4))

10

> (foldl + '(1 2 3 4))

10

> (foldr + '(1 2 3 4) #:order 'bab)

10

> (foldl + '(1 2 3 4) #:order 'bab)

10

> (foldr .. '("hi" " " "there"))

"hi there"

> (foldl .. '("hi" " " "there"))

"there hi"

> (foldr cons '(1 2 3) #:into '() #:order 'abb)

'(1 2 3)

> (foldl cons '(1 2 3) #:into '() #:order 'abb)

'(3 2 1)

> (foldr cons '(1 2 3) #:into '() #:order 'bab)

'(((() . 3) . 2) . 1)

> (foldl cons '(1 2 3) #:into '() #:order 'bab)

'(((() . 1) . 2) . 3)

procedure

(fold/steps f    
  seqs    
  [#:into base    
  #:order order    
  #:direction direction])  any/c
  f : (-> any/c any/c any/c)
  seqs : (listof (sequenceof any/c))
  base : any/c = #f
  order : (one-of/c 'abb 'bab) = 'abb
  direction : (one-of/c 'left 'right) = 'right

procedure

(foldl/steps f    
  seqs    
  [#:into base    
  #:order order])  any/c
  f : (-> any/c any/c any/c)
  seqs : (listof (sequenceof any/c))
  base : any/c = #f
  order : (one-of/c 'abb 'bab) = 'abb

procedure

(foldr/steps f    
  seqs    
  [#:into base    
  #:order order])  any/c
  f : (-> any/c any/c any/c)
  seqs : (listof (sequenceof any/c))
  base : any/c = #f
  order : (one-of/c 'abb 'bab) = 'abb
Similar to foldl/steps, but, like fold, infers the relevant identity element where possible and uses it as the base value, if none is provided. foldl/steps is equivalent to calling fold/steps with #:direction 'left, and foldr/steps is equivalent to calling fold/steps with #:direction 'right.

Examples:
> (->list (fold/steps + '(1 2 3 4)))

'(0 4 7 9 10)

> (->list (foldr/steps + '(1 2 3 4)))

'(0 4 7 9 10)

> (->list (foldl/steps + '(1 2 3 4)))

'(0 1 3 6 10)

> (->list (foldr/steps * '(1 2 3 4)))

'(1 4 12 24 24)

> (->list (foldl/steps * '(1 2 3 4)))

'(1 1 2 6 24)

> (->list (foldr/steps .. '("hi" " " "there")))

'("" "there" " there" "hi there")

> (->list (foldl/steps .. '("hi" " " "there")))

'("" "hi" " hi" "there hi")

procedure

(sum vs)  addable?

  vs : (listof addable?)
Equivalent to (apply + vs), this supports numbers in the usual way, but also supports any other addable type, for instance, vectors.

Examples:
> (sum (list 1 2 3 4))

10

> (sum (list #(1 2 3) #(1 2 3) #(1 2 3)))

'#(3 6 9)

procedure

(product vs)  multipliable?

  vs : (listof multipliable?)
Equivalent to (apply * vs), this supports numbers in the usual way, but also supports any other multipliable type.

Example:
> (product (list 1 2 3 4))

24