Miscellaneous
Printing options
AbstractAlgebra supports printing to LaTeX using the MIME type "text/latex". To enable LaTeX rendering in Jupyter notebooks and query for the current state, use the following functions:
set_html_as_latex — Functionset_html_as_latex(fl::Bool)Toggles whether MIME type text/html should be printed as text/latex. Note that this is a global option. The return value is the old value.
get_html_as_latex — Functionget_html_as_latex()Returns whether MIME type text/html is printed as text/latex.
Updating the type diagrams
Updating the diagrams of the documentation can be done by modifying and running the script docs/create_type_diagrams.jl. Note that this requires the package Kroki.
Attributes
Often it is desirable to have a flexible way to attach additional data to mathematical structures such as groups, rings, fields, etc. beyond what the original implementation covers. To facilitate this, we provide an attributes system: for objects of suitable types, one may use set_attribute! to attach key-value pairs to the object, and query them using has_attribute, get_attribute and get_attribute!.
Attributes are supported for all singletons (i.e., instances of an empty struct type), as well as for instances of mutable struct type for which attribute storage was enabled. There are two ways to enable attribute storage for such types:
- By applying
@attributesto a mutable struct declaration, storage is reserved inside that struct type itself (this increases the size of each struct by 8 bytes if no attributes are set). - By applying
@attributesto the name of a mutable struct type, methods are installed which store attributes to instances of the type in aWeakKeyDictoutside the struct.
@attributes — Macro@attributes typedefThis is a helper macro that ensures that there is storage for attributes in the type declared in the expression typedef, which must be either a mutable struct definition expression, or the name of a mutable struct type.
The latter variant is useful to enable attribute storage for types defined in other packages. Note that @attributes is idempotent: when applied to a type for which attribute storage is already available, it does nothing.
For singleton types, attribute storage is also supported, and in fact always enabled. Thus it is not necessary to apply this macro to such a type.
When applied to a struct definition this macro adds a new field to the struct. For structs without constructor, this will change the signature of the default inner constructor, which requires explicit values for every field, including the attribute storage field this macro adds. Usually it is thus preferable to add an explicit default constructor, as in the example below.
Examples
Applying the macro to a struct definition results in internal storage of the attributes:
julia> @attributes mutable struct MyGroup
order::Int
MyGroup(order::Int) = new(order)
end
julia> G = MyGroup(5)
MyGroup(5, #undef)
julia> set_attribute!(G, :isfinite, :true)
julia> get_attribute(G, :isfinite)
trueApplying the macro to a typename results in external storage of the attributes:
julia> mutable struct MyOtherGroup
order::Int
MyOtherGroup(order::Int) = new(order)
end
julia> @attributes MyOtherGroup
julia> G = MyOtherGroup(5)
MyOtherGroup(5)
julia> set_attribute!(G, :isfinite, :true)
julia> get_attribute(G, :isfinite)
true@attr — Macro@attr [RetType] funcdefThis macro is applied to the definition of a unary function, and enables caching ("memoization") of its return values based on the argument. This assumes the argument supports attribute storing (see @attributes) via get_attribute!.
The name of the function is used as name for the underlying attribute.
Effectively, this turns code like this:
@attr RetType function myattr(obj::Foo)
# ... expensive computation
return result
endinto something essentially equivalent to this:
function myattr(obj::Foo)
return get_attribute!(obj, :myattr) do
# ... expensive computation
return result
end::RetType
endExamples
julia> @attributes mutable struct Foo
x::Int
Foo(x::Int) = new(x)
end;
julia> @attr Int function myattr(obj::Foo)
println("Performing expensive computation")
return factorial(obj.x)
end;
julia> obj = Foo(5);
julia> myattr(obj)
Performing expensive computation
120
julia> myattr(obj) # second time uses the cached result
120
has_attribute — Functionhas_attribute(G::Any, attr::Symbol)Return a boolean indicating whether G has a value stored for the attribute attr.
get_attribute — Functionget_attribute(f::Function, G::Any, attr::Symbol)Return the value stored for the attribute attr, or if no value has been set, return f().
This is intended to be called using do block syntax.
get_attribute(obj, attr) do
# default value calculated here if needed
...
endget_attribute(G::Any, attr::Symbol, default::Any = nothing)Return the value stored for the attribute attr, or if no value has been set, return default.
get_attribute! — Functionget_attribute!(f::Function, G::Any, attr::Symbol)Return the value stored for the attribute attr of G, or if no value has been set, store key => f() and return f().
This is intended to be called using do block syntax.
get_attribute!(obj, attr) do
# default value calculated here if needed
...
endget_attribute!(G::Any, attr::Symbol, default::Any)Return the value stored for the attribute attr of G, or if no value has been set, store key => default, and return default.
set_attribute! — Functionset_attribute!(G::Any, data::Pair{Symbol, <:Any}...)Attach the given sequence of key=>value pairs as attributes of G.
set_attribute!(G::Any, attr::Symbol, value::Any)Attach the given value as attribute attr of G.
The attributes system can be utilized to change the way certain objects are printed. We provide macros @show_special and @show_name for this purpose, both are called with the same argument as show: an IO-object and the object itself. Both are supposed to be used within the usual show function:
function show(io::IO, A::MyObj)
@show_name(io, A)
@show_special(io, A)
... usual stuff@show_special checks if an attribute :show_special is present. If so, it has to be a function taking IO and the object. This is then called instead of the usual show function.
@show_name will check if there is a variable in global (Main module) namespace with value bound to the object. In compact printing mode, the name is then shown instead of the object.
Note: if the object is stored in several variable, the first one will be used. Also the name, once used for printing, is stored in the object - hence will not change anymore.
Advanced printing
To facilitate printing of nested mathematical structures, we provide a modified IOCustom object, that supports indentation and decapitalization.
Example
We illustrate this with an example
struct A{T}
x::T
end
function Base.show(io::IO, a::A)
io = AbstractAlgebra.pretty(io)
println(io, "Something of type A")
print(io, AbstractAlgebra.Indent(), "over ", AbstractAlgebra.Lowercase(), a.x)
print(io, AbstractAlgebra.Dedent()) # don't forget to undo the indentation!
end
struct B
end
function Base.show(io::IO, b::B)
io = AbstractAlgebra.pretty(io)
print(io, LowercaseOff(), "Hilbert thing")
endAt the REPL, this will then be printed as follows:
julia> A(2)
Something of type A
over 2
julia> A(A(2))
Something of type A
over something of type A
over 2
julia> A(B())
Something of type A
over Hilbert thingDocumentation
pretty — Functionpretty(io::IO) -> IOCustomWrap io into an IOCustom object.
Examples
julia> io = AbstractAlgebra.pretty(stdout);Indent — TypeIndentWhen printed to an IOCustom object, increases the indentation level by one.
Examples
julia> io = AbstractAlgebra.pretty(stdout);
julia> print(io, AbstractAlgebra.Indent(), "This is indented")
This is indentedDedent — TypeDedentWhen printed to an IOCustom object, decreases the indentation level by one.
Examples
julia> io = AbstractAlgebra.pretty(stdout);
julia> print(io, AbstractAlgebra.Indent(), AbstractAlgebra.Dedent(), "This is indented")
This is indentedLowercase — TypeLowercaseWhen printed to an IOCustom object, the next letter printed will be lowercase.
Examples
julia> io = AbstractAlgebra.pretty(stdout);
julia> print(io, AbstractAlgebra.Lowercase(), "Foo")
fooLowercaseOff — TypeLowercaseOffWhen printed to an IOCustom object, the case of the next letter will not be changed when printed.
Examples
julia> io = AbstractAlgebra.pretty(stdout);
julia> print(io, AbstractAlgebra.Lowercase(), AbstractAlgebra.LowercaseOff(), "Foo")
Foo