Function Clauses
A predicate like [(> ?speed 100)] only filters. A function clause goes one step further: it calls a function and binds the result to a new variable, [(function arg ...) ?result]. The new variable can be used by later clauses, returned in :find, or grouped and counted, like any other.
Arithmetic
A Pokemon's base stat total is the sum of its six base stats. + accepts as many arguments as we give it. Here is every Pokemon with a total of 600 or more. The first function clause binds ?total, and the predicate after it filters on it:
Watch out for integer division
Dividing one integer by another drops the fractional part rather than rounding down: Datomic's / truncates toward zero, like Clojure's quot. For negative numbers that differs from a floor: (/ -7 2) is -3, not -4. It also isn't a ratio like Clojure's own / would give: dividing 11 by 9 here gives the integer 1, not 11/9. Multiply one side by 1.0 first to get a decimal. Mewtwo's attack is 110 and its defense is 90:
Strings
str joins values into a string. Here it builds a label from a Pokemon's number and name:
subs takes part of a string, from a start index up to (but not including) an end index. Bound values can be grouped like any variable, so we can use count, as in Cardinality many, to see how many Pokemon start with each letter. Datalog groups by everything in :find that isn't an aggregate:
clojure.string/upper-case and clojure.string/lower-case are available too.
Defaults with get-else
Only a few Pokemon have a :pokemon/category. A clause that uses the attribute leaves out every Pokemon without it. get-else keeps them, and supplies a default value instead. It takes the database, an entity, an attribute and the default:
get-else has two limits worth knowing. The attribute must be cardinality one: asking it for :pokemon/type (cardinality many) refuses with cardinality-many attrs not supported: :pokemon/type. And the default can't be nil: that refuses too, with nil default value not supported. Use a sentinel value like :none instead.
The next chapter, Negation, shows the opposite: missing? keeps only the Pokemon that lack the attribute.
Lower the total in the first query to 580 to see the legendary birds join the list. Or wrap the label in clojure.string/upper-case with another function clause. Or change :pokemon/category to :pokemon/type in the get-else query above to see the cardinality-many error for yourself.
- Bind a new variable to the result of a function with a function clause, and reuse it in later clauses or
:find. - Know that Datomic's
/truncates toward zero rather than giving a true division. - Use
get-elseto supply a default for a missing cardinality-one attribute, and know its two limits.