Prining of types

17 views
Skip to first unread message

Waldek Hebisch

unread,
Jul 23, 2026, 8:46:07 PMJul 23
to fricas...@googlegroups.com
Our old code for printing types is rather complicated and works
differently than code doing coercion of types to OutputForm.

Attached is a patch which extended new code doing coercion of
types to InputForm and OutputForm to also produce string
(needed by the interpreter to print types). By default
this code uses Format1D to produce string representation
of values, but if you do:

)boot $use_old_value_print := true

it will use old code to produce string from OutputForm.

The reason for this switch is to allow easy comparison of the
two methods. Also, Format1D in some cases produces much
longer strings. The cases I observed are matrices, where
old code produces something like:

(5) -> i := e(1)$C


(5) e
1
Type: CliffordAlgebra(1,Fraction(Polynomial(Integer)),[[-1]])

while Format1D leads to:

(6) e
1
Type: CliffordAlgebra(1,Fraction(Polynomial(Integer)),matrix[[-1]])

Similar thing happens with subscripts, Format1D produces
'subscript(x, [1])' while old code just produces 'x[1]'. That
blows up types using scripted symbols.

Note that even if normally the code use Format1D, some cases are sent
to old code. And old code is called directly from some parts of the
interpreter. Still, is could be a step towards eventually
removing old code.

--
Waldek Hebisch
sum6a2.diff

Ralf Hemmecke

unread,
Jul 24, 2026, 3:03:46 AMJul 24
to fricas...@googlegroups.com
On 7/24/26 02:46, Waldek Hebisch wrote:
> Also, Format1D in some cases produces much
> longer strings. The cases I observed are matrices, where
> old code produces something like:
>
> (5) -> i := e(1)$C
>
>
> (5) e
> 1
> Type: CliffordAlgebra(1,Fraction(Polynomial(Integer)),[[-1]])
>
> while Format1D leads to:
>
> (6) e
> 1
> Type: CliffordAlgebra(1,Fraction(Polynomial(Integer)),matrix[[-1]])
>
> Similar thing happens with subscripts, Format1D produces
> 'subscript(x, [1])' while old code just produces 'x[1]'. That
> blows up types using scripted symbols.

Hmmmm... I think, it should be possible to modify Format1D at least for
this special subscript case, i.e. a symbol with arguments. And I think
that would even be better, because my intention was that Format1D should
not only be a 1D output but in most cases should also allow to cut&paste
it back into a FriCAS session.

For (6) I have no easy solution. Format1D just transforms what it sees
in OutputForm. If that shows a matrix as the third argument of
CliffordAlgebra, then it has to produce a matrix. Format1D does not know
that it is producing a type.
So, if you cheat in the conversion of the matrix argument to an
OutputForm by letting it be a list of lists, then the output would be
the same. But I am not sure whether this can be done easily if
CliffordAlgebra appear in a nested type.

I am happy that Format1D eventually becomes even more useful.

Ralf

PS:

Oh, the function in Format1D is this:

subscript(p: Z): H == (prec: Z, args: LE): BOX +->
b1: BOX := formatExpression(args.1, MIN)
b2: BOX := formatExpression(args.2, MIN)
formatFunction(box "subscript", [b1, parenthesize("[", "]", b2)])

I guess, what you need is something like (pseudo-code below)

if symbol? args.1 then ... else ...

would that be enough? The point is that the b1 box should be a simple
expression so that just concatenating b1 with [b2] gives the intented
form. So the question is "how does Format1D recognize a 'simple' b1?"

Cannot already Lisp symbols look rather complicated

Actually, testing for symbol might not be enough, because of

%%% (32) -> sy := INTERN("foo(u[e]")$Lisp

(32) foo(u[e]
Type: SExpression
%%% (33) -> sy::OutputForm pretend SEX

(33) foo(u[e]
Type: SExpression

OK, that is certainly not the standard way to produce symbols in FriCAS.

Ralf Hemmecke

unread,
Jul 24, 2026, 5:39:07 AMJul 24
to fricas...@googlegroups.com
> Oh, the function in Format1D is this:
>
>   subscript(p: Z): H == (prec: Z, args: LE): BOX +->
>     b1: BOX := formatExpression(args.1, MIN)
>     b2: BOX := formatExpression(args.2, MIN)
>     formatFunction(box "subscript", [b1, parenthesize("[", "]", b2)])
>
> I guess, what you need is something like (pseudo-code below)
>
>    if symbol? args.1 then ... else ...

Arrrh, I just see, that there is no way to find out whether args.1 is a
symbol other than cheating with

symbol?(args.1 pretend SEX)

or first converting to InputForm and then checking.
But hey, the latter does not help.

%%% (46) -> symbol?(('x::OutputForm) pretend SEX)

(46) true

%%% (47) -> symbol?(convert('x::OutputForm)@InputForm)

(47) false

Another simple test can be to extract the string from the box b1 and
check whether it is just consisting of letters, numbers and underscores.

Other suggestions?

Ralf

Waldek Hebisch

unread,
Jul 24, 2026, 6:10:33 AMJul 24
to 'Ralf Hemmecke' via FriCAS - computer algebra system
On Fri, Jul 24, 2026 at 09:03:43AM +0200, 'Ralf Hemmecke' via FriCAS - computer algebra system wrote:
> On 7/24/26 02:46, Waldek Hebisch wrote:
> > Also, Format1D in some cases produces much
> > longer strings. The cases I observed are matrices, where
> > old code produces something like:
> >
> > (5) -> i := e(1)$C
> >
> >
> > (5) e
> > 1
> > Type: CliffordAlgebra(1,Fraction(Polynomial(Integer)),[[-1]])
> >
> > while Format1D leads to:
> >
> > (6) e
> > 1
> > Type: CliffordAlgebra(1,Fraction(Polynomial(Integer)),matrix[[-1]])
> >
> > Similar thing happens with subscripts, Format1D produces
> > 'subscript(x, [1])' while old code just produces 'x[1]'. That
> > blows up types using scripted symbols.
>
> Hmmmm... I think, it should be possible to modify Format1D at least for this
> special subscript case, i.e. a symbol with arguments. And I think that would
> even be better, because my intention was that Format1D should not only be a
> 1D output but in most cases should also allow to cut&paste it back into a
> FriCAS session.

Yes, I understand why Format1D is doing this. AFAICS there are two
different goals, first goal is compatibility with input, second
goal is compact linear output. That would suggest two different
domains. Since most of code should be common probably we could
add a parameter to the domain.

> For (6) I have no easy solution. Format1D just transforms what it sees in
> OutputForm. If that shows a matrix as the third argument of CliffordAlgebra,
> then it has to produce a matrix. Format1D does not know that it is producing
> a type.

It is really not "producing a type", but rather wanting shorter output
even if it can not be read back. The Boot code if requested will
produce InputForm which should evaluate back to the type. Unparsing
this input form in the case above leads to:

CliffordAlgebra(1,Fraction(Polynomial(Integer())),squareMatrix(matrix([[-1]])))

so is even longer. But goal of string output is to produce string
to put in 'Type: ' line.

> So, if you cheat in the conversion of the matrix argument to an OutputForm
> by letting it be a list of lists, then the output would be the same. But I
> am not sure whether this can be done easily if CliffordAlgebra appear in a
> nested type.

Boot code in question traverses the type. When it sees argument
which is not a type, then it needs to call back to algebra code.
So it does not matter if types are nested. But complicated arguments
involving subexpressions may be more complicated.

In CliffordAlgebra case interpreter konws required types of arguments,
so can coerce list of lists to matrix. In more complicated cases
interpreter may be unable to find appropriate coercions.

> I am happy that Format1D eventually becomes even more useful.
>
> Ralf
>
> PS:
>
> Oh, the function in Format1D is this:
>
> subscript(p: Z): H == (prec: Z, args: LE): BOX +->
> b1: BOX := formatExpression(args.1, MIN)
> b2: BOX := formatExpression(args.2, MIN)
> formatFunction(box "subscript", [b1, parenthesize("[", "]", b2)])
>
> I guess, what you need is something like (pseudo-code below)
>
> if symbol? args.1 then ... else ...
>
> would that be enough?

For subscripts the attached patch gives desired effect.

> The point is that the b1 box should be a simple
> expression so that just concatenating b1 with [b2] gives the intented form.
> So the question is "how does Format1D recognize a 'simple' b1?"
>
> Cannot already Lisp symbols look rather complicated
>
> Actually, testing for symbol might not be enough, because of
>
> %%% (32) -> sy := INTERN("foo(u[e]")$Lisp
>
> (32) foo(u[e]
> Type: SExpression
> %%% (33) -> sy::OutputForm pretend SEX
>
> (33) foo(u[e]
> Type: SExpression
>
> OK, that is certainly not the standard way to produce symbols in FriCAS.

I think that we can assume reasonable symbols. It is really
responsibility of algbera authors and users to ensure that
nothing like above is produced as part of OutputForm.

--
Waldek Hebisch
FMT1D.diff

Qian Yun

unread,
Jul 24, 2026, 7:35:00 AMJul 24
to fricas...@googlegroups.com
Looks good according to LLM.

Only one minor typo: Intepreter -> Interpreter.

- Qian

Ralf Hemmecke

unread,
Jul 24, 2026, 8:01:09 AMJul 24
to fricas...@googlegroups.com
>> Hmmmm... I think, it should be possible to modify Format1D at least for this
>> special subscript case, i.e. a symbol with arguments. And I think that would
>> even be better, because my intention was that Format1D should not only be a
>> 1D output but in most cases should also allow to cut&paste it back into a
>> FriCAS session.

> Yes, I understand why Format1D is doing this. AFAICS there are two
> different goals, first goal is compatibility with input, second
> goal is compact linear output. That would suggest two different
> domains. Since most of code should be common probably we could
> add a parameter to the domain.

Oh, I am fine with two domains. Whether with parameter or one domain
inherits from the other or both from a common category (with default
domain) or a common domain is to be discussed. Important is that

setFormats! in FormattedOutput should work.

https://fricas.github.io/api/FormattedOutput.html

Currently, I am somewhat against a parametrized domain, so that it would
be easier to convert a string just a domain name without parameter) into
a formatting type. (Note at some time in the future we might want to
deals with the Format... domains in the same way as with ")set output
algebra on/off".

But yes, we can have something like Format1DInput and Format1D where the
first follows the goal of possibly producing output that can be
cut&paste back to a session and Format1D would be just showing nice
compact linear output. Perfectly fine with me.



>> For (6) I have no easy solution. Format1D just transforms what it sees in
>> OutputForm. If that shows a matrix as the third argument of CliffordAlgebra,
>> then it has to produce a matrix. Format1D does not know that it is producing
>> a type.
>
> It is really not "producing a type",

Well, I meant "it doesn't know that it is formatting a type".

> so is even longer. But goal of string output is to produce string
> to put in 'Type: ' line.

Yes, we can work on this. But I have the impression the respective
formatting domain must either get a better OutputForm as input or (which
I find somewhat better, we introduce a mechanism that enables each type
to export a function of how it wants to be printed, i.e. something like

Foo(X: DomX, Y: DomY, pol: Polynomial(X)): with
constructor_name: () -> String
...
== add ...

where constructor_name() returns the type.

Yes, it is probably not the right way to do it. The default
implementation should just give the name "Foo" of the domain (and
perhaps their domain parameters). Maybe this is what the boot code is doing.

However, what I have in mind is that we do not have a global boot
procedure that deals with every domain perfectly, but rather allow
package/domain/category writers to implement a "compact" type
expression. Sorry, I have no idea how that would integrate with SPAD.
It's just an idea for more flexibility to produce more compact types.

In fact, sometimes I wished that in a session I could enter some command
that appreviates a complicated type like
Fraction(UnivariatePolynomial('x,Integer)) to RF(Integer,'x) or even
RFZx and the type printing mechanism should apply my abbreviation wishes.

I'd find that much more attractive than working on finding a mechanism
to just printing some non-domain parameters in a shorter format.

> For subscripts the attached patch gives desired effect.

That's fine with me.

Ralf

Waldek Hebisch

unread,
Jul 24, 2026, 9:46:05 AMJul 24
to 'Ralf Hemmecke' via FriCAS - computer algebra system
On Fri, Jul 24, 2026 at 02:01:05PM +0200, 'Ralf Hemmecke' via FriCAS - computer algebra system wrote:
> > > Hmmmm... I think, it should be possible to modify Format1D at least for this
> > > special subscript case, i.e. a symbol with arguments. And I think that would
> > > even be better, because my intention was that Format1D should not only be a
> > > 1D output but in most cases should also allow to cut&paste it back into a
> > > FriCAS session.
>
> > Yes, I understand why Format1D is doing this. AFAICS there are two
> > different goals, first goal is compatibility with input, second
> > goal is compact linear output. That would suggest two different
> > domains. Since most of code should be common probably we could
> > add a parameter to the domain.
>
> Oh, I am fine with two domains. Whether with parameter or one domain
> inherits from the other or both from a common category (with default domain)
> or a common domain is to be discussed. Important is that
>
> setFormats! in FormattedOutput should work.
>
> https://fricas.github.io/api/FormattedOutput.html
>
> Currently, I am somewhat against a parametrized domain, so that it would be
> easier to convert a string just a domain name without parameter) into a
> formatting type. (Note at some time in the future we might want to deals
> with the Format... domains in the same way as with ")set output algebra
> on/off".

We can define "shortcut" domains like this:

Format1D == Format1DInternal(true)

ATM it seems that I need to look more into parameter passing,
because 'false' in similar construct apparently in Spad works the same
as 'true' ('false' from interpreter seem to work).
That would need pattern matching on parts of type. Case above looks
easy to match. Main trouble is with matching hairy values (as some
users have noticed pattern matching for expressions has its quirks).

Today you can do

)boot $abbreviateTypes := true

and get:

Fraction(UnivariatePolynomial('x,Integer))

(18) FRAC(UP(x,INT))

We could extend this machinery, as long as we deal with "pure" types
it is not hard because we essentially deal with a tree. Values
are more problematic as they can be anything.

> I'd find that much more attractive than working on finding a mechanism to
> just printing some non-domain parameters in a shorter format.
>
> > For subscripts the attached patch gives desired effect.
>
> That's fine with me.
>
> Ralf

--
Waldek Hebisch

Ralf Hemmecke

unread,
Jul 24, 2026, 11:34:40 AMJul 24
to fricas...@googlegroups.com
On 7/24/26 15:46, Waldek Hebisch wrote:
>> Oh, I am fine with two domains. Whether with parameter or one domain
>> inherits from the other or both from a common category (with default domain)
>> or a common domain is to be discussed. Important is that
>>
>> setFormats! in FormattedOutput should work.
>>
>> https://fricas.github.io/api/FormattedOutput.html
>>
>> Currently, I am somewhat against a parametrized domain, so that it would be
>> easier to convert a string just a domain name without parameter) into a
>> formatting type. (Note at some time in the future we might want to deals
>> with the Format... domains in the same way as with ")set output algebra
>> on/off".
>
> We can define "shortcut" domains like this:
>
> Format1D == Format1DInternal(true)

Yes, OK, if that makes sense.

Nevertheless, I still think that telling Format1D to generate things
differently, is not the right way. Format1D works on OutputForm. It is
just "formatting" what OutputForm says.

AFAIU, the machinery that translates a constructor structure into
OutputForm, i.e.

constructor_to_OutputForm(con) == constructor_to_form(con, 'out_form)

should, for example take care of producing an appropriate OutputForm
that is then formatted nicely by Format1D. In other words,

((CliffordAlgebra(1,Fraction(Polynomial(Integer)),matrix
[[-1]]))::OutputForm) pretend SEX

should not give this

(CliffordAlgebra 1 (Fraction (Polynomial Integer)) (MATRIX () (ROW - 1)))

but rather this

(CliffordAlgebra 1 (Fraction (Polynomial Integer)) (BRACKET (AGGLST
(BRACKET (AGGLST - 1)))))

Then Format1D will generate

CliffordAlgebra(1,Fraction(Polynomial(Integer)),[[-1]])

Do you think that changing the generation of the OutputForm for a
constructor is the way to go? I think so.

Otherwise create a domain, Format1D, that generates for
matrix([[a,b],[c,d]]) always just "[[a,b],[c,d]]" (not only in type
arguments) and another one, Format1DInput, that generates
"matrix([[a,b],[c,d]])".

>> In fact, sometimes I wished that in a session I could enter some command
>> that appreviates a complicated type like
>> Fraction(UnivariatePolynomial('x,Integer)) to RF(Integer,'x) or even RFZx
>> and the type printing mechanism should apply my abbreviation wishes.

> That would need pattern matching on parts of type. Case above looks
> easy to match. Main trouble is with matching hairy values (as some
> users have noticed pattern matching for expressions has its quirks).

Well, pattern matching on constructurs with domain arguments would
already make me partially happy. And if for some non-domain parameters
pattern matching does not work, then it is just not improving the
current situation. In total, anything that allows to finetune the
prining of types as a user I would count as progress.

In my work I came across types that were quite longish with repeated
subdomains. To really check the argument structure then is quite
cumbersome and

> )boot $abbreviateTypes := true

does not really help there.

> We could extend this machinery, as long as we deal with "pure" types
> it is not hard because we essentially deal with a tree. Values
> are more problematic as they can be anything.

I don't know what exactly you propose. What is in my opinion important
is that a user can influence how a type is printed, i.e. the constructor
tree structure should be documented and public and
constructor_to_OutputForm should live in a SPAD package with options for
a user to modify the transformation at runtime.
In the end it is tree rewriting. Maybe RuleSet can help there. (Sorry, I
am just dreaming a bit.)

Ralf

Waldek Hebisch

unread,
Jul 25, 2026, 7:18:55 PMJul 25
to 'Ralf Hemmecke' via FriCAS - computer algebra system
On Fri, Jul 24, 2026 at 05:34:36PM +0200, 'Ralf Hemmecke' via FriCAS - computer algebra system wrote:
> On 7/24/26 15:46, Waldek Hebisch wrote:
> > > Oh, I am fine with two domains. Whether with parameter or one domain
> > > inherits from the other or both from a common category (with default domain)
> > > or a common domain is to be discussed. Important is that
> > >
> > > setFormats! in FormattedOutput should work.
> > >
> > > https://fricas.github.io/api/FormattedOutput.html
> > >
> > > Currently, I am somewhat against a parametrized domain, so that it would be
> > > easier to convert a string just a domain name without parameter) into a
> > > formatting type. (Note at some time in the future we might want to deals
> > > with the Format... domains in the same way as with ")set output algebra
> > > on/off".
> >
> > We can define "shortcut" domains like this:
> >
> > Format1D == Format1DInternal(true)
>
> Yes, OK, if that makes sense.

Yes, it makes sense. I am not fully sure about Boolean, having
a symbol would be more general, but currently we need only two
possibilities and Boolean is sufficient.

> Nevertheless, I still think that telling Format1D to generate things
> differently, is not the right way. Format1D works on OutputForm. It is just
> "formatting" what OutputForm says.
>
> AFAIU, the machinery that translates a constructor structure into
> OutputForm, i.e.
>
> constructor_to_OutputForm(con) == constructor_to_form(con, 'out_form)
>
> should, for example take care of producing an appropriate OutputForm that is
> then formatted nicely by Format1D. In other words,
>
> ((CliffordAlgebra(1,Fraction(Polynomial(Integer)),matrix
> [[-1]]))::OutputForm) pretend SEX
>
> should not give this
>
> (CliffordAlgebra 1 (Fraction (Polynomial Integer)) (MATRIX () (ROW - 1)))
>
> but rather this
>
> (CliffordAlgebra 1 (Fraction (Polynomial Integer)) (BRACKET (AGGLST (BRACKET
> (AGGLST - 1)))))
>
> Then Format1D will generate
>
> CliffordAlgebra(1,Fraction(Polynomial(Integer)),[[-1]])
>
> Do you think that changing the generation of the OutputForm for a
> constructor is the way to go? I think so.

Current design is that generation of OutputForm is delegated to
various domain. They should provide consistent high level description
of the output. Choice between formatting possibilties is
resposiblity of formatters. So, it is the formatter that decides
if brackets alone are sufficient or if the word 'matix' is
appropriate. And the same OutputForm should be usable with all
formatters.

> Otherwise create a domain, Format1D, that generates for
> matrix([[a,b],[c,d]]) always just "[[a,b],[c,d]]" (not only in type
> arguments) and another one, Format1DInput, that generates
> "matrix([[a,b],[c,d]])".

Yes, logially we need two different formatters.

> > > In fact, sometimes I wished that in a session I could enter some command
> > > that appreviates a complicated type like
> > > Fraction(UnivariatePolynomial('x,Integer)) to RF(Integer,'x) or even RFZx
> > > and the type printing mechanism should apply my abbreviation wishes.
>
> > That would need pattern matching on parts of type. Case above looks
> > easy to match. Main trouble is with matching hairy values (as some
> > users have noticed pattern matching for expressions has its quirks).
>
> Well, pattern matching on constructurs with domain arguments would already
> make me partially happy. And if for some non-domain parameters pattern
> matching does not work, then it is just not improving the current situation.
> In total, anything that allows to finetune the prining of types as a user I
> would count as progress.

OK, I will try to do this.

> In my work I came across types that were quite longish with repeated
> subdomains. To really check the argument structure then is quite cumbersome
> and
>
> > )boot $abbreviateTypes := true
>
> does not really help there.
>
> > We could extend this machinery, as long as we deal with "pure" types
> > it is not hard because we essentially deal with a tree. Values
> > are more problematic as they can be anything.
>
> I don't know what exactly you propose. What is in my opinion important is
> that a user can influence how a type is printed, i.e. the constructor tree
> structure should be documented and public and constructor_to_OutputForm
> should live in a SPAD package with options for a user to modify the
> transformation at runtime.

ATM it is not clear to me what would be the best user interface.
I will try first something crude and then we can try to improve
it to usable level.

> In the end it is tree rewriting. Maybe RuleSet can help there. (Sorry, I am
> just dreaming a bit.)


--
Waldek Hebisch

Ralf Hemmecke

unread,
Jul 26, 2026, 10:39:22 AMJul 26
to fricas...@googlegroups.com
On 7/26/26 01:18, Waldek Hebisch wrote:
> Current design is that generation of OutputForm is delegated to
> various domain. They should provide consistent high level description
> of the output. Choice between formatting possibilties is
> resposiblity of formatters.
Hmmmm... I would be happy with such a distinction, but unfortunately, it
is often not so clear what the language of OutputForm actually is.

Look at this
https://github.com/fricas/fricas/blob/r1.3.13/src/algebra/contfrac.spad#L306

%%% (4) -> (((113/337::ContinuedFraction(Integer))::OutputForm)pretend SEX)

(4) (+ (+ (+ (ZAG 1 2) (ZAG 1 1)) (ZAG 1 55)) (ZAG 1 2))

How would a formatter have any chance to typeset this according to a
certain format? That form is basically already saying that it should be
printed in a linear format not like a continued fraction like in the
picture on wikipedia.

https://en.wikipedia.org/wiki/Continued_fraction#Formulation

There is a top-level tag missing. Otherwise the formatter must go
through a +-Structure and find that there are ZAG subexpressions.

Here clearly the domain ContinuedFraction already decided about an
output format, it's not just providing tagged data.

I somehow think that vconcat and hconcat should not be functions in
OutputForm, because they tell something about visual appearance.
That all probably needs longer discussions. At the moment it's good
enough, but not perfect.

Ralf

Waldek Hebisch

unread,
Jul 28, 2026, 4:02:22 PMJul 28
to 'Ralf Hemmecke' via FriCAS - computer algebra system
After earlier disscussion, attached is proposed modification to
Format1D. It modifies main constructur to have name 'Format1DInternal'
and a parameter. It adds two shortcuts:

Format1D == Format1DInternal(false)

Format1DInput == Format1DInternal(true)

As suggested Format1DInput reproduce former behaviour, Format1D
produces somewhat shorted output. The proposed change to
main constructor is identical to previous one.

As I mentioned, there was trouble with using Boolean as a
parameter, but it is now fixed in the trunk.

BTW: I took quick look at 'scripts'. For output "scripts(f, [z, +, z, +])"
I get

scripts(f, [z, +, z, +])
There are no exposed library operations named scripts having 2
argument(s) though there are 1 unexposed operation(s) with 2
argument(s). Use HyperDoc Browse, or issue
)display op scripts
to learn more about the availableoperations.

Cannot find a definition or applicable library operation named
scripts with argument type(s)
Variable(f)
List(OrderedVariableList([z,+]))

Perhaps you should use "@" to indicate the required return type,
or "$" to specify which version of the function you need.

OTOH
script(f, [[z], [+], [z], [+]])

z +
(9) f
+ z
Type: Symbol
produces thing that looks like original OutputForm.

--
Waldek Hebisch
sum6a3.diff
Reply all
Reply to author
Forward
0 new messages