operators::overloaded
--
conversions and overloaded functions
The library operators::overloaded
provides a framework for conversions
between domains. It also allows for defining multiparameter
overloaded functions.
operators::overloaded
is a library which allows for
declaring and using conversions between MuPAD domains.
Those conversions can be implicit or explicit. operators::overloaded::convertExplicit
). They are not
applied transitively.operators::overloaded
is also a domain whose objects are multi-parameter
overloaded functions f
, which may have several simultaneous
implementations with associated signatures (here, a
signature consists of a list of MuPAD domains, which
represent the domains of the arguments; the domain of the result
is not included in this signature). When such a function is called
as f(a,b,c)
, it looks up the list of the domains of its
arguments a
, b
, and c
(as returned by
domtype
), tries to match them with the available signatures,
and chooses accordingly the appropriate implementation.operators::overloaded
::convertExplicit
replace the standard convert
function. Feedback is particularly welcome.
operators::overloaded(default, <signatures>
)
default | - | A function. |
signatures | - | A table associating implementations to signatures. |
operators::overloaded
(default, <signatures>)
returns a new
overloaded function, with default
as default
implementation, and with signatures and corresponding
implementations taken from signatures
.
declareDomain(
MuPAD domain F
)
declareConversion(
MuPAD domain F,
MuPAD domain G,
function conversion
<, option>
)
F
to the
domain G
implemented by conversion
. The optional
parameter option
can be either Explicit, to declare an
explicit conversion, or describe the weight of the conversion.
This weight can be a positive (non-negative?) rational (real?)
number, or one of the predefined weights Defaultor Facade.
Until proper weight scales are proposed, we strongly recommend to
only use those predefined weights.DOM_INT
)
into a facade domain (say Dom::Integer
); in this case,
conversion
should be the identity id
.conversion
should be a function taking an element of the
domain F
, and returning an element of the domain G
(note that, for a facade domain G
, the domain of an element
of G
may differ from G
). If the conversion cannot be
done, an error should be raised.F
to
G
, the former conversion is silently overwritten.
convert(object, MuPAD domain G)
object
into the domain G
using only
implicit conversions, if this is possible; otherwise, returns
FAIL
. In the case where G
is a facade domain, the
domain of the result may differ from G
.
convertExplicit(object, MuPAD domain G)
convert
, but both implicit and explicit
conversions may be applied.new
or of the method
convert
of a domain. See also the category
Cat::UseOverloading
.
conversion(MuPAD domain F, MuPAD domain G)
F
and
G
. The result is a list of functions [f1,...,fk]
such that an element x
of F
can be converted into an
element of G
by (fk@...@f2@f1)(x)
. If there is no
such conversion, FAIL
is returned.conversion(MuPAD domain F, MuPAD domain G, Cost)
FAIL
.
printConversions()
declareSignature(
overloaded function f,
list of MuPAD domains signature,
function implementation
)
f
,
implemented by implementation
.We start by using some predefined conversions. Here is how to convert an integer into an element of Z/3Z using the canonical projection:
.>> Z3 := Dom::IntegerMod(3):
a := operators::overloaded::convert(7, Z3)
1 mod 3
Then, we can embed Z/3Z into an algebraic extension, and do the conversion:
>> K := Dom::AlgebraicExtension(Z3, X^2=1):
b := operators::overloaded::convert(a, K);
domtype(b)
1 2 Dom::AlgebraicExtension(Dom::IntegerMod(3), X - 1 = 0, X)
Being aware of the two basic conversions above, the system can apply them transitively:
>> operators::overloaded::convert(7, K)
1
Currently only the natural conversions between the basic MuPAD domains are predefined in the system:
>> operators::overloaded::printConversions()
Dom::IntegerMod(3) -> Dom::AlgebraicExtension(Dom::IntegerMod(3), X^2 - 1 = 0, X) DOM_COMPLEX -> Dom::Complex Dom::Float -> Dom::Complex Dom::Rational -> Dom::Complex DOM_EXPR -> Dom::ExpressionField() DOM_IDENT -> Dom::ExpressionField() Dom::Complex -> Dom::ExpressionField() Dom::Real -> Dom::ExpressionField() DOM_FLOAT -> Dom::Float Dom::Rational -> Dom::Float DOM_INT -> Dom::Integer Dom::Integer -> Dom::IntegerMod(3) DOM_RAT -> Dom::Rational Dom::Integer -> Dom::Rational Dom::Float -> Dom::Real Dom::Rational -> Dom::Real
Most of those conversions are actually trivial, since the
target domains are facade domains like Dom::Integer
.
Furthermore, for parametrized domains like
Dom::IntegerMod(3)
, the conversions are declared on the fly
when the domains are created.
It is easy to extend the system to deal with new domains and
conversions. We define the ring P
of univariate polynomials
over K
, declare it to the conversion system, and declare the
canonical embedding from the field K
::
>> P := Dom::UnivariatePolynomial(x, K):
operators::overloaded::declareDomain(P):
operators::overloaded::declareConversion(K, P,
c -> multcoeffs(P::one, c)):
Now, we indifferently can do conversions from the integers,
from Z3
, or from K
into P
:
>> c := operators::overloaded::convert(7, P);
domtype(c)
1 Dom::UnivariatePolynomial(x, Dom::AlgebraicExtension( 2 Dom::IntegerMod(3), X - 1 = 0, X), LexOrder)
We define a new overloaded function myconcat
which, by
default, returns an unevaluated call:
>> myconcat := operators::overloaded
(() -> (userinfo(0, "default implementation"); hold(myconcat)(args())),
table([DOM_STRING, DOM_STRING] =
((s1,s2) -> (userinfo(0, "concatenating strings"); s1.s2)),
[DOM_LIST, DOM_LIST] =
((s1,s2) -> (userinfo(0, "concatenating lists"); s1.s2)))):
myconcat
can be used indifferently to concatenate two
strings or two lists:
>> setuserinfo(Any, 1):
setuserinfo(Graph, 0):
myconcat("bla", "ble");
myconcat([1, 2, 3], [4, 5, 6])
Info: concatenating strings "blable" Info: concatenating lists [1, 2, 3, 4, 5, 6]
By default, an unevaluated call is returned:
>> myconcat(35, 73)
Info: default implementation myconcat(35, 73)
Let us extend the concatenation to integers, in a non standard way:
>> operators::overloaded::declareSignature
(myconcat,
[Dom::Integer, Dom::Integer],
(x, y) -> (userinfo(0, "concatenating integers");
text2expr(expr2text(x).expr2text(y)))):
myconcat(35, 73)
Info: concatenating integers 3573
Of course, this is not very well defined, in particular if the second argument is negative!
>> myconcat(35, -73)
Info: concatenating integers 35 - 73
>> setuserinfo(Any, 0):
The point of all this is just to highlight the fact that the programmer should be very careful with the semantic of the conversions and overloaded operators he or she defines. The system does not check that they are consistent in any way; in most cases that would be impossible.
To conclude here are some examples of not-so-well defined conversions that are likely to produce problems in a larger scale environment:
>> operators::overloaded::declareDomain(DOM_IDENT):
operators::overloaded::declareDomain(DOM_STRING):
operators::overloaded::declareConversion(DOM_IDENT, DOM_STRING, expr2text):
myconcat(bla, "ble");
myconcat("bla", ble);
myconcat(bla, ble)
"blable" "blable" "blable"
Note in particular what happens in the last computation: the system did not know how to concatenate two identifiers; so it decided to lift both identifiers into strings, and concatenate them as strings. In some cases, this behavior is perfectly acceptable (the result may, for example, live in a domain that is isomorphic to the domain of at least one of the operands), but in most cases it is not. In a future version of this library, we plan to provide means to forbid such conversions.
Overloaded functions shows the so-called a reference effect. This is required to be able to add new signatures to an existing overloaded function. Technically, this is achieved through closures.
Dom::Integer
to Dom::Rational
(natural embedding
of sets);
DOM_INT
to Dom::Integer
(system representation
to facade domains);
Dom::Rational
to Dom::UnivariatePolynomial([x],
Dom::Rational)
(natural embedding from the ground field of a
ring with one);
Dom::Rational
to
Dom::AlgebraicExtension(Dom::Rational, x^2=2)
(natural
embedding of a field into an extension field);
Dom::Integer
to Dom::IntegerMod(4)
(canonical
projection)
(examples::SymmetricFunctions()
)::p
to
(examples::SymmetricFunctions()
)::s
canonical
isomorphisms between the different representations of a fixed
ring over several basis.
On the other hand, the conversion from the indexes of a basis of a vector space to this vector space (say, for example, from partitions to monomial symmetric functions) are usually not canonical, and in most cases should not be declared as implicit.
In most cases, those rules of thumb are enough for the programmer for deciding whether a conversion should be implicit or explicit. However a more subtle issue can arise: to be on the safe side, an implicit conversion A↔B should be a morphism for each and every overloaded function that is defined on B.
MuPAD Combinat, an open source algebraic combinatorics package