axiom-developer
[Top][All Lists]

## Re: [Axiom-developer] Curiosities with Axiom mathematical structures

 From: Ralf Hemmecke Subject: Re: [Axiom-developer] Curiosities with Axiom mathematical structures Date: Wed, 08 Mar 2006 17:01:42 +0100 User-agent: Thunderbird 1.5 (X11/20051201)

On 03/07/2006 08:52 PM, William Sit wrote:

Ralf Hemmecke wrote:

For example, assume we have

define ContainerType(T: Type): Category == with {
bracket: Tuple Type -> %;    -- constructor
generator: % -> Generator T; -- yields all the elements
#: % -> Integer;             -- size
default {
#(x: %): Integer == {
n: Integer := 0;
for element in x repeat n := n + 1;
}
return n;
}
}

define FixedArrayType(n: Integer, T: Type): Category == with {
ContainerType(T);
default {#(x: %): Integer == n;}
}



Now, assume I have some other category MyFancyCat that inherits from
ContainerType, but not from FixedArrayType. Then I create

define MyFixedCat(n: Integer, T: Type): Category == with {
MyFancyCat T;
FixedArrayType(n, T);
}

Of course, MyFixedCat also inherits defaults, but which one? (The
current compiler takes the first one.) Changing the order of the
categories results in more efficient code. However, think of a second
default function that could be implemented in both categories, but the
more efficient one in ContainerType. Then you can choose any order for
MyFixedCat and get always only one efficient default.



In case of multiple inheritance, it is the responsibility of the user to choose,
not asking the compiler to choose for you (that would be equivalent to asking
the Interpreter to coerce and then complain about the result or resulting type).
It would be trivial to override (and choose) by something like:

default(#: %)->Integer == #$(MyFancyCat T)  or  default(#:% ->Integer == #$FixedArrayType(n,T)

The compiler should not choose this and should flag it as a user error.


Ah, I like your idea with the package calling mechanism via

$(MyFancyCat(T) Unfortunately, the current Aldor compiler rejects that. "defaultChoice.as", line 28: #(x: %): Integer == #(x)$FixedArrayType(n, T);
............................^^

[L28 C29] #2 (Error) There are no suitable meanings for the operator #$FixedArrayType(n, T)'. [L28 C30] #1 (Error) No one possible return type satisfies the context type. These possible return types were rejected:  -- Category == ContainerType(T) with default #(x: %): AldorIntege...  The context requires an expression of type with . And from Section 8.3 of the Aldor User Guide http://www.aldor.org/docs/HTML/chap8.html#3  it is not completely obvious that$ couldn't also be used with categories (instead of domains) as the second argument.


But your suggestion is great. As with other things if the compiler detects ambiguities, it should simply stop and tell that, or to be a bit more user friendly give a warning so that a programmer can override the default if he/she does not like what the compiler chooses.


Anyway, if there where some control over the "default"s that would be an advantage.


When I started with Aldor, I used defaults a lot, but I encountered
exactly the above scenario and then removed the defaults in almost all
places and shifted it to the actual domains. Default implementations
should be used with great care.


Are you telling me that Aldor does not allow overriding a default
implementation? I know Axiom allows.



No. SPAD and Aldor behave in the same way. Any specific implementation in a domain overrides the category defaults.


At the time I used it, I wanted to avoid to duplicate code, so I tried to give efficient default implementations in categories. Until I discovered the problem that I could not select which default would be used.


Inheritance of behaviour from domains might seem a bit strange for people coming from other programming languages.

Quiz: What is the output of the following code? Explain why!

--quiz.as
#include "aldor"
#include "aldorio"
Cat: Category == with {
coerce: Integer -> %;
<<: (TextWriter, %) -> TextWriter;
foo: % -> Integer;
}
Rep == Integer;
import from Integer;
coerce(i: Integer): % == per i;
foo(x: %): Integer == rep x;
(tw: TextWriter) << (x: %): TextWriter == tw << foo(x);
}

Dom2A: Cat == DomA add {
Rep == Integer;
import from Integer;
foo(x: %): Integer == 2*(rep x);
}

main(): () == {
import from Integer, DomA, Dom2A;
stdout << "DomA  " << (2::DomA ) << newline;
stdout << "Dom2A " << (3::Dom2A) << newline;
}
main();
--end quiz.as


How to handle renaming is of course a very difficult problem. The idea, in both
mathematics and symbolic computation, to overload operators (symbols) is to
reduce the number of different notations when the operators have essentially the
same properties. Let's not even discuss the complication that arises when
typesetting (which is when \cdot  or \times or simply concatenation may be used
for the same multiplication).  Thus * is commonly used for commutative
multiplication and sometimes for noncommutative multiplication as well, + is
always for addition, \circ for composition (non-commutative of course), \cdot
for matrix product or vector dot product, etc.  When an algebraic structure
involves more than one kind of multiplication, sometimes mathematicians "abuse"
notations by not distinguishing them, like k(yz) could mean a scalar
multiplication by k to an algebra product y and z.  In computer algebra, this is
clearly not allowed because, as I said, computer languages are rigid.



Well, I think you are wrong with the last sentence. If y and z are polynomials over integers and k is an integer (in Aldor or Axiom) then it is already possible to write "k*(y*z)". And if one exploits the syntactic sugar features of Aldor and defines functions

macro P == UnivariatePolynomial Integer;
apply: (Integer, P) -> P;
apply: (P, P) -> P;

then one could even write "k(y z)".


But you probably know that interally the function name of "*" includes its type.


By the way, I don't follow his example at bottom of p. 127: If \phi is a functor
(he said homomorphism, but that is wrong, because a list and a set are from
different categories), he wants the equation to hold:

\phi(#([1,1]) = #(\phi([1,1]))

The right hand side makes sense, with answer 1, but the left hand side does not,
since #([1,1]) = 2 is a number, not a list. He claimed \phi(2) = 2.



Wow, exactly that point puzzled me, too. But maybe the important thing is that the design of a good category and domain hierarchy is not straightforward if an automated coercion routine should be possible.

But I won't say much more since I haven't really studied the details.

Ralf

`