Custom Operators
This tutorial will demonstrate how to implement custom operators using the operator Interface. This interface can be used to implement operators with custom behavior such as:
- Number of parameters
- Lazy and eager evaluation
- Semantics
- Associativity
- Initial value
- Pretty printing
- Side-effects
This tutorial is not yet polished. This interface is incomplete and will be changed in v0.4.
Setup
Implementing an operator requires defining methods for that operator. To do so, their function names must be imported or prefixed by the Interface
module. This module also exports several other required and useful functions.
julia> import PAndQ: Associativity, Evaluation, dual, evaluate, initial_value, parenthesize, print_expression, symbol
WARNING: could not import PAndQ.Evaluation into Main
julia> using PAndQ, .Interface
Nullary
This is a renamed tautology
operator. First, define an Operator
. If possible, this should be a const
ant whose name corresponds to the operator name.
julia> const truth = Operator{:truth}()
Error showing value of type Operator{:truth}:
ERROR: InterfaceError: implement `symbol` for `Operator{:truth}()`
If a required method is not implemented, a runtime error will display the function and operator that a method must be implemented for. The error says to implement symbol
. This function is used to print an operator.
julia> symbol(::typeof(truth)) = "truth";
ERROR: UndefVarError: `truth` not defined
julia> truth
ERROR: UndefVarError: `truth` not defined
julia> truth()
ERROR: UndefVarError: `truth` not defined
The error says to implement Evaluation
. This function is used to specify whether an operator lazily or eagerly evaluates its arguments.
julia> Evaluation(::typeof(truth)) = Lazy;
julia> truth()
Error showing value of type PAndQ.Tree{0}:
ERROR: InterfaceError: implement `print_expression` for `Operator{:truth}()` with `0` propositions
The error says to implement print_expression
. This function is used to print a node of a syntax tree.
julia> print_expression(io, o::typeof(truth), ps) = show(io, "text/plain", o);
ERROR: UndefVarError: `truth` not defined
julia> truth()
ERROR: UndefVarError: `truth` not defined
julia> print_table(truth())
ERROR: UndefVarError: `truth` not defined
The error says to implement evaluate
. This function is used to specify the semantics of an operator.
julia> evaluate(::typeof(truth), ps) = ⊤;
ERROR: UndefVarError: `truth` not defined
julia> print_table(truth())
ERROR: UndefVarError: `truth` not defined
Unary
This is an eagerly evaluated not
operator.
julia> const negate = Operator{:negate}();
ERROR: TypeError: in Type{...} expression, expected UnionAll, got Type{PAndQ.Interface.Operator}
julia> symbol(::typeof(negate)) = "negate";
ERROR: UndefVarError: `negate` not defined
julia> negate
ERROR: UndefVarError: `negate` not defined
julia> Evaluation(::typeof(negate)) = Eager;
ERROR: UndefVarError: `negate` not defined
julia> evaluate(::typeof(negate), ps) = evaluate(¬, ps);
ERROR: UndefVarError: `negate` not defined
julia> @atomize negate(¬p)
ERROR: MethodError: objects of type PAndQ.AbstractSyntaxTree are not callable
julia> @atomize print_table(negate(p))
ERROR: MethodError: objects of type PAndQ.AbstractSyntaxTree are not callable
Binary
This is an imply
operator represented by the -->
symbol.
julia> const if_then = --> = Operator{:if_then}();
ERROR: TypeError: in Type{...} expression, expected UnionAll, got Type{PAndQ.Interface.Operator}
julia> symbol(::typeof(-->)) = "-->";
ERROR: UndefVarError: `-->` not defined
julia> -->
ERROR: UndefVarError: `-->` not defined
julia> Evaluation(::typeof(-->)) = Lazy;
ERROR: UndefVarError: `-->` not defined
If a node in a syntax tree is not the root node, it may be necessary to parenthesize it to avoid ambiguity. The parenthesize
function is used to print parentheses around a node if it is not the root node. The print_proposition
function is used to print the propositions in a node.
julia> print_expression(io, o::typeof(-->), ps) = parenthesize(io) do print_proposition(io, first(ps)) print(io, " ") show(io, "text/plain", o) print(io, " ") print_proposition(io, last(ps)) end;
ERROR: UndefVarError: `-->` not defined
julia> @atomize p --> q
ERROR: UndefVarError: `-->` not defined
julia> evaluate(::typeof(-->), ps) = first(ps) → last(ps);
ERROR: UndefVarError: `-->` not defined
julia> @atomize print_table(p --> q)
ERROR: UndefVarError: `-->` not defined
julia> @atomize fold(𝒾, (-->) => ())
ERROR: KeyError: key PAndQ.AbstractSyntaxTree(:-->) not found
This error says to implement Associativity
. This function is used to determine which direction to fold
.
julia> Associativity(::typeof(-->)) = Left;
ERROR: UndefVarError: `-->` not defined
julia> @atomize fold(𝒾, (-->) => ())
ERROR: KeyError: key PAndQ.AbstractSyntaxTree(:-->) not found
This error says to implement initial_value
. This function is used to determine the init
parameter when fold
ing.
julia> initial_value(::typeof(-->)) = ⊤;
ERROR: UndefVarError: `-->` not defined
julia> @atomize fold(𝒾, (-->) => ())
ERROR: KeyError: key PAndQ.AbstractSyntaxTree(:-->) not found
julia> @atomize fold(𝒾, (-->) => (p, q, r))
ERROR: KeyError: key PAndQ.AbstractSyntaxTree(:-->) not found
Ternary
This is a lazily evaluated conditional operator.
julia> const conditional = Operator{:conditional}();
ERROR: TypeError: in Type{...} expression, expected UnionAll, got Type{PAndQ.Interface.Operator}
julia> symbol(::typeof(conditional)) = "?";
ERROR: UndefVarError: `conditional` not defined
julia> conditional
ERROR: UndefVarError: `conditional` not defined
julia> Evaluation(::typeof(conditional)) = Lazy;
ERROR: UndefVarError: `conditional` not defined
julia> print_expression(io, o::typeof(conditional), ps) = parenthesize(io) do print_proposition(io, ps[1]) print(io, " ? ") print_proposition(io, ps[2]) print(io, " : ") print_proposition(io, ps[3]) end;
ERROR: UndefVarError: `conditional` not defined
julia> @atomize conditional(p, q, r)
ERROR: MethodError: objects of type PAndQ.AbstractSyntaxTree are not callable
julia> function evaluate(::typeof(conditional), ps) p, q, r = ps (p → q) ∧ (p ∨ r) end;
ERROR: UndefVarError: `conditional` not defined
julia> @atomize print_table(conditional(p, q, r))
ERROR: MethodError: objects of type PAndQ.AbstractSyntaxTree are not callable