# Reductions In Tomo, reductions are a way to express the idea of folding or reducing a collection of values down to a single value. Reductions use an infix operator surrounded by parentheses, followed by a collection: ```tomo nums := [10, 20, 30] sum := (+) nums >> sum = 60 ``` Reductions can be used as an alternative to generic functions like `sum()`, `product()`, `any()`, and `all()` in Python, or higher-order functions like `foldl` and `foldr` in functional programming: ```tomo # Sum: >> (+) [10, 20, 30] = 60 # Product: >> (*) [2, 3, 4] = 24 # Any: >> (or) [no, yes, no] = yes # All: >> (and) [no, yes, no] = no ``` ## Minimum and Maximum Reductions are _especially_ useful for finding the minimum or maximum values in a collection using the `_min_` and `_max_` infix operators. ```tomo # Get the maximum value: >> (_max_) [10, 30, 20] = 30 # Get the minimum value: >> (_min_) [10, 30, 20] = 10 ``` The `_min_` and `_max_` operators also support field and method call suffixes, which makes it very easy to compute the argmin/argmax (or keyed minimum/maximum) of a collection. This is when you want to get the minimum or maximum value _according to some feature_. ```tomo # Get the longest text: >> (_max_.length) ["z", "aaaaa", "mmm"] = "aaaaa" # Get the number with the biggest absolute value: >> (_max_:abs()) [1, -2, 3, -4] = -4 ``` ## Comprehensions Reductions work not only with iterable values (arrays, sets, integers, etc.), but also with comprehensions. You can use comprehensions to perform reductions while filtering out values or while applying a transformation: ```tomo # Sum the lengths of these texts: >> (+) t.length for t in ["a", "bc", "def"] = 6 # Sum the primes between 1-100: >> (+) i for i in 100 if i:is_prime() = 1060 ``` ## Empty Collection Behavior If a collection has no members, the default behavior for a reduction is to create a runtime error and halt the program with an informative error message. If you instead want to provide a default fallback value, you can use `else:` to give one: ```tomo empty := [:Int] >> (+) empty else: -1 = -1 >> (+) empty # Error: empty iterable! ``` You can also provide your own call to `fail()` or `exit()` with a custom error message, or a short-circuiting control flow statement (`return`, `stop`, `skip`) like this: ```tomo >> (_max_) things else: exit("No things!") for nums in num_arrays: product := (*) nums else: skip do_thing(product) func remove_best(things:[Thing]): best := (_max_.score) things else: return best:remove() ```