Ralf explained well overall design, but let me restate it. Since our
domains can be composed in recursive way the only resonable way to
produce output is via cooperation of domains, each domain is responsible
for its part calling recursively output function for domains that
it uses. OutputForm has tree structure and one can easily compose
parts. Of course, it is possible to do composition on level of
strings, but it is more clumsy. Important point is that we have
multiple output formats. Independent implementation of 'latex'
would create significant code duplication: each domain would need
higher level logic very similar to current logic for producing
OutputForm. But beside higher level logic there would be specific
handling for latex. This would require more code than current
design. Also, note that currently formatter has full control
of produced strings, so it can assume that they have specific
structure. Just image possible complications if domain author
decides that he/she wants to redefine some basic LaTeX operator.
Coming back to multiple formats: we have several output formats
and can be resonably sure that they work. More precisely, once
formatter correctly handles operators from OutputForm it can
handle output from any domain. In alternative world with
specialised formatters there is danger that domain authors would
decide to leave some formats unimplemented. You have examples
how it works in practice: in NAG sources 'latex' worked for
few 'major' domains but was mostly unimplmented. Simply domain
authors decided not to bother implementing 'latex'. And we
have InputForm which has substantial gaps. Since uses of
InputForm are limited there is limited motivation to fill the
gaps. Coming back to output, imagine that one author only
implements text output for domain D1. Another author
only implements LaTeX output for parametrised domain D2.
Now, even if D2(D1) works in sense of doing computations
you are unable to get any output from it: since D2 only
supports LaTeX you can get output from D2(T) only if T
supports LaTeX, which fails for D1 having only text output.
While missing LaTeX output have low probability, we have
MathML output and it is much more likely that domain author
would skip this. Or Texmacs interface: it depends on
specialized formatter and I bet that most domain authors
would not bother implementing Texmacs output.
I understand that you would prefer to avoid implementing proper
conversion to OutputForm. After all, while not hard it is
clearly more work than faking it using strings. But looking
at big picture, somebody has to ensure that various parts of
FriCAS work together. For formatting in FriCAS this work is
minimised by having OutputForm and coercion to OutputForm
in each domain.
> I mean, to build OutputForm you need to
> > be able to dissect data into parts, recursively convert each part to
> > OutputForm and combinie then back. If this process of dissection
> > is too complicated, then what useful can you do with such data?
>
> I can do whatever I want, in fact at different levels, FriCAS, Lisp,
> Julia or the engine behind. I will do that later. I am at the
> "thinking about" stage. But basically and that raises interesting
> questions to me, I am not at all a big fan of rotten coding, I use a
I do not know what "rotten coding" is supposed to mean.
> strictly formal way of representing my expressions, and they are
> _only_ evaluated at output time. That will change for some routines.
Hmm, that looks horribly inefficient.
Consider
z := big_expr - big_expr_written_in_different_way
(z = 0)@Boolean
Do you mean that each time code/user test if 'z = 0' you repeat
computations? There is concept of lazy evaluation, but normally
lazy evaluation keeps things unevaluated as long as it can, but
when "forced" it evaluets them and further computations use
evaluated result.
> I use simplify with an assumption below, I do not have in mind right
> now a better example without simplify, it's a pity. As far as the
> expression is not evaluated, FriCAS has it its full form in a String:
>
> (1) -> x:=jWSExpr("x")
>
> (1) x
> Type: JuliaWSExpression
> (2) -> x^(jWSRat 1/7)+sqrt(x^2)
>
> (2) x^(1/7) + Sqrt[x^2]
> Type: JuliaWSExpression
> (3) -> simplifiedEx:=simplify(%, "x<0")
>
> (3) x^(1/7) - x
> Type: JuliaWSExpression
> (4) -> string %
>
> (4)
> "W`Simplify[Plus[Power[x, Rational[1, 7]], Sqrt[Power[x, 2]]], Less[x, 0]]`"
> Type: String
> (5) -> jlSymbolic simplifiedEx
>
> (5) "Plus[Power[x, Rational[1, 7]], Times[-1, x]]"
> Type: String
> (6) -> toString simplifiedEx
>
> (6) "x^(1/7) - x"
> Type: JuliaWSExpression
That is not entirely clear. I mean, due to assignment in (3) in (4)
% and simplifiedEx should be the same. So do you mean that
string simplifiedEx
and
toString simplifiedEx
produce different results? And do you mean that 'toString' (and
presumably 'jlSymbolic' is re-doing computations? Or do you
remember "everything" and just recall previously computed result
(but apparently keep all history of comutations)?
> (7) -> toString(simplifiedEx,"InputForm")
>
> (7) "x^(1/7) - x"
> Type: JuliaWSExpression
> (8) -> jlSymbolic %%(2)
>
> (8) "Plus[Power[x, Rational[1, 7]], Power[Power[x, 2], Rational[1, 2]]]"
> Type: String
> (9) -> %%(2)
>
> (9) x^(1/7) + Sqrt[x^2]
> Type: JuliaWSExpression
>
> These output to string routines are useful to better know the
> different internal structures.
>
> Behind the scenes, in Julia, W"Sqrt" produces the Julia WS language
> symbol 'Sqrt' that can be applied to the symbol x when they are sent
> to the engine.
--
Waldek Hebisch