curry.toolchain.flat2icurry.bindingopt module
The binding optimization, a FlatCurry-to-FlatCurry pass over the FlatCurry file of a module.
PAKCS and KiCS2 run the tool transbooleq over every FlatCurry file they
compile (the property bindingoptimization of PAKCS, fast by
default). This module is a port of its fast mode. The ideas are in “From
Boolean Equalities to Constraints” (Antoy and Hanus, LOPSTR 2015) and
“Transforming Boolean equalities into constraints” (Formal Aspects of
Computing, 2017).
A Boolean equality whose value is required to be True is replaced by the equational constraint, which binds free variables where the Boolean equality on a primitive type suspends:
e1 == e2ande1 === e2becomeconstrEq e1 e2.
e1 /= e2ande1 /== e2with required value False becomenot (constrEq e1 e2).
The required value comes from the context. The condition of a rule is
required True: the front end writes a guard as case c of True -> e;
False -> failed. The scrutinee of a case with one branch that does not
fail is required to be the constructor of that branch. The fast mode
knows the required values of the arguments of a few Prelude operations and
no others (PRELUDE_REQUIRED_VALUES): the arguments of && and &
required True are required True, the argument of not required False is
required True, and so on. An operation outside the table requires nothing
of its arguments. So f x | x == 3 = x and f x | x == 1 && y == 2
bind, while if x == 3 then a else b and a guard with an otherwise
branch keep the Boolean equality, as under PAKCS.
The pass changes the semantics of a program in the same way PAKCS does.
(1 == 2) & True is False under the plain semantics and fails after the
pass, because & requires both arguments True whatever its result is
required to be. An equivalence == is replaced on the assumption that
the Eq instance defines equality; the tool of PAKCS makes the same
assumption.
optimize_file() applies the pass to a FlatCurry file in place, as
PAKCS does before any compiler reads the file (preprocessFcyFile of its
compiler): the file is written again when the pass replaced an equality, and
left as it is otherwise. Both routes from Curry to ICurry
(curry.toolchain._frontend and the icurry program, see
curry.toolchain._curry2icurry) run it after the front end and before
the translation, as the first pass of the rewrite step (rewrite;
the set0 lift of setlift is the second), so the .fcy file on
disk is the optimized program and the two routes translate the same text.
The pass is idempotent: over an optimized program it replaces nothing and
writes nothing. optimize_bindings() is the pass on a program in
memory; the oracle tests of the translation never apply it, because the
files icurry wrote for the oracle come from the FlatCurry as the front
end wrote it.
The command line of rewrite rewrites FlatCurry files by hand, with
both passes:
python -m curry.toolchain.flat2icurry.rewrite [-q] M.fcy...
The products made from the file before (the ICurry and what follows it)
are not touched by it; sprite-make --rewrite-flat runs the step of a
module again and makes them from the rewritten file.
- curry.toolchain.flat2icurry.bindingopt.case_arg_type(branches)[source]
The required value of the scrutinee of a case. True when the second branch is
False -> failed, the translation of a guard. Otherwise the constructor of the one branch that does not fail, or ANY.
- curry.toolchain.flat2icurry.bindingopt.constructors(a)[source]
The constructor set of a constructor value.
- curry.toolchain.flat2icurry.bindingopt.contains_equality(e, equivalence=True)[source]
Whether an expression holds a Boolean equality or disequality call. The walk keeps its own stack, so a nested application of any depth needs no recursion.
- curry.toolchain.flat2icurry.bindingopt.equality_operands(e, eq, equivalence=True)[source]
The two operands of a call of a Boolean equality (
eq) or disequality (noteq), or None. Three shapes count: a saturated call of the method with the operands last, after any dictionaries; a method applied to a dictionary and then to the operands throughapply; and a default instance method without a dictionary, applied the same way.
- curry.toolchain.flat2icurry.bindingopt.lub(a, b)[source]
The least upper bound of two abstract values, lubAType of the analysis. ANY absorbs every value, ANYC absorbs a constructor value, and two constructor values join to the set of their constructors: a single name when both name the same constructor, else a frozenset. The join of True and False is such a set, not ANYC: it is the required value of the argument of
notas a value, and it requires nothing of the arguments of a call below it (seerequired_argument_values).
- curry.toolchain.flat2icurry.bindingopt.optimize_bindings(prog, equivalence=True)[source]
Applies the binding optimization to a FlatCurry program. Returns the transformed program when an equality was replaced, else
progitself: transbooleq writes a file only then, so a rule whose equalities all stay is rebuilt (its$reduced) only in a program that is transformed.
- curry.toolchain.flat2icurry.bindingopt.optimize_file(fcyfile, equivalence=True)[source]
Applies the binding optimization to the FlatCurry file
fcyfilein place. The file is written again, in the format of the front end, when the pass replaced an equality; else it is left as it is, with its time. Returns the number of equalities replaced.
- curry.toolchain.flat2icurry.bindingopt.required_argument_values(qname, reqval, nargs)[source]
The required values of the
nargsarguments of a call ofqnamewhose result is required to bereqval: argumentTypesFor of transbooleq. The table gives the arguments of the entry with that result, else of an entry with an unrequired result, else the join of all entries whenreqvalis ANY or ANYC. An operation outside the table, and any other case, requires nothing. A set of constructors is such a case:notas a value requires True or False of its argument, sonot ((x == 1) & (y == 2))andnot (solve (x == 3))keep their equalities and suspend, as under PAKCS, whilenot (not e)requires nothing ofe, and the join path transforms&andsolvein it.
- curry.toolchain.flat2icurry.bindingopt.transform_exp(e, reqval, equivalence=True)[source]
Transforms an expression whose value is required to be
reqval. Returns the new expression and the number of equalities replaced. The walk runs on a stack of its own (utility.trampoline.trampoline()), so a nested application of any depth needs no recursion (issue #125).
- curry.toolchain.flat2icurry.bindingopt.transform_func(fd, equivalence=True)[source]
Transforms a function declaration. Returns it with the count. As in transbooleq, a rule without a Boolean equality is returned as it is, and a rule with one is rebuilt, so its applications of
$to a partial application are reduced to calls whether or not an equality is replaced.