This package defines some functions that do logical composition of predicate functions (i.e., functions that return a boolean). These are higher-order functions, i.e., functions that take functions as arguments and return a new function. Julia implements them efficiently. This permits a compact notation for selecting specific elements out of collections.
AND(f1,f2) = (args...) -> f1(args...) && f2(args...)
OR(f1,f2) = (args...) -> f1(args...) || f2(args...)These functions give functions that compare the output values of different functions. The last three (MAX, MIN, SUM) can take an arbitrary number of arguments, and create functions that (normally) return numerical, not logical, values.
ISEQUAL(f1,f2) = (args...) -> f1(args...) == f2(args...)
ISLESS(f1,f2) = (args...) -> f1(args...) < f2(args...)
ISLESSEQ(f1,f2) = (args...) -> f1(args...) ≤ f2(args...)
ISGREATER(f1,f2) = (args...) -> f1(args...) > f2(args...)
ISGREATEREQ(f1,f2) = (args...) -> f1(args...) ≥ f2(args...)
MAX(fs...) = (args...) -> max((f(args...) for f in fs)...)
MIN(fs...) = (args...) -> min((f(args...) for f in fs)...)
SUM(fs...) = (args...) -> +((f(args...) for f in fs)...)For convenient typing, we also define the following aliases using operators that do not have a predefined meaning in Julia.
⩓ = AND(type with\And<Tab>)⩔ = OR(type with\Or<Tab>)≣ = ISEQUAL(type with\Equiv<Tab>)≺ = ISLESS(type with\prec<Tab>)⪯ = ISLESSEQ(type with\preceq<Tab>)≻ = ISGREATER(type with\succ<Tab>)⪰ = ISGREATEREQ(type with\succeq<Tab>)
CAUTION: The operators ⩓ and ⩔, although they do not have predefined meaning, have the precedence of addition operators, which is higher than comparison operators like ≻ and ≺.
So a statement like length ≻ 2 ⩓ length ≺ 7 would be parsed as length ≻ (2 ⩓ length) ≺ 7, i.e., the function x -> length(x) > (2(x) && length(x)) < 7(x), instead of the probably desired x -> (length(x) > 2) && (length(x) < 7).
So to logically compose functions like this, you have to explicitly add parentheses, e.g., (length ≻ 2) ⩓ (length ≺ 7).
For a complete list of operator precedence rules, see https://github.com/JuliaLang/julia/blob/master/src/julia-parser.scm.
length_from_3_to_5 = (length ⪰ 3) ⩓ (length ⪯ 5)which is equivalent to
length_from_3_to_5(x) = length(x) ≥ 3 && length(x) ≤ 5The compact notation in particular is advantageous for passing as a function argument to filter and similar functions, compare
filter((length ⪰ 3) ⩓ (length ⪯ 5), collection)to
filter(x -> length(x) ≥ 3 && length(x) ≤ 5, collection)Assuming we have functions count_apples, count_oranges, and count_lemons that count the number of apples, oranges, and lemons in some data structure describing a fruit basket, we can do
filter(MAX(count_apples,count_oranges,count_lemons) ⪰ 3, fruit_baskets)instead of
filter(x -> max(count_apples(x),count_oranges(x),count_lemons(x)) ⪰ 3, fruit_baskets)to get all fruit baskets with at least three apples, three oranges, or three lemons.