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, symbolWARNING: could not import PAndQ.Evaluation into Mainjulia> using PAndQ, .Interface
Nullary
This is a renamed tautology operator. First, define an Operator. If possible, this should be a constant 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 definedjulia> truthERROR: UndefVarError: `truth` not definedjulia> 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` propositionsThe 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 definedjulia> truth()ERROR: UndefVarError: `truth` not definedjulia> 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 definedjulia> 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 definedjulia> negateERROR: UndefVarError: `negate` not definedjulia> Evaluation(::typeof(negate)) = Eager;ERROR: UndefVarError: `negate` not definedjulia> evaluate(::typeof(negate), ps) = evaluate(¬, ps);ERROR: UndefVarError: `negate` not definedjulia> @atomize negate(¬p)ERROR: MethodError: objects of type PAndQ.AbstractSyntaxTree are not callablejulia> @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 definedjulia> -->ERROR: UndefVarError: `-->` not definedjulia> 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 definedjulia> @atomize p --> qERROR: UndefVarError: `-->` not definedjulia> evaluate(::typeof(-->), ps) = first(ps) → last(ps);ERROR: UndefVarError: `-->` not definedjulia> @atomize print_table(p --> q)ERROR: UndefVarError: `-->` not definedjulia> @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 definedjulia> @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 folding.
julia> initial_value(::typeof(-->)) = ⊤;ERROR: UndefVarError: `-->` not definedjulia> @atomize fold(𝒾, (-->) => ())ERROR: KeyError: key PAndQ.AbstractSyntaxTree(:-->) not foundjulia> @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 definedjulia> conditionalERROR: UndefVarError: `conditional` not definedjulia> Evaluation(::typeof(conditional)) = Lazy;ERROR: UndefVarError: `conditional` not definedjulia> 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 definedjulia> @atomize conditional(p, q, r)ERROR: MethodError: objects of type PAndQ.AbstractSyntaxTree are not callablejulia> function evaluate(::typeof(conditional), ps) p, q, r = ps (p → q) ∧ (p ∨ r) end;ERROR: UndefVarError: `conditional` not definedjulia> @atomize print_table(conditional(p, q, r))ERROR: MethodError: objects of type PAndQ.AbstractSyntaxTree are not callable