bug? macro expansion in interpreter

9 views
Skip to first unread message

Ralf Hemmecke

unread,
Feb 13, 2026, 5:53:17 PM (10 days ago) Feb 13
to fricas-devel
Hi Waldek,

This is a follow-up to

https://groups.google.com/g/fricas-devel/c/uiXRlAq7-aI/m/VOP8UVCFDgAJ

It is of similar type (macro function, i.e. macro that returns a macro),
but yields an error just in the interpreter and only under very specific
instances.

That looks like a bug that is hopefully easy to fix.
I first thought it was connected to using C in the definition of (3),
but then I switched to CC and the problem

The function BOOT::|%pform| is undefined.

still appears, but only if CC expands to a type with a parameter like
"Fraction(ZZ)".

It would make me very happy if this problem can be fixed. I need the
situation where CC is QQ.

Anyhow, looking at the session below somehow seems to tell me that CC in
(3) is *not* a local parameter whose name is irrelevant.

OK, maybe in (3) I should just use C_unused_elsewhere_in_the_program
instead of CC and be happy.

Nevertheless, I consider the problem as a bug.

Ralf

=============================================================================
FriCAS Computer Algebra System
Version: FriCAS e266e8d2319808c04583010380d138eb36c276fd built with sbcl
2.2.9.debian
-----------------------------------------------------------------------------

%%% (1) -> ZZ ==> Integer

Type: Void
%%% (2) -> QQ ==> Fraction(ZZ)

Type: Void
%%% (3) -> An(vl)(CC) ==> DistributedMultivariatePolynomial(vl, CC)

Type: Void
%%% (4) -> Xn(vl)(CC) ==> Record(aa: An(vl)(CC), bb: String)

Type: Void
%%% (5) -> An(['x,'y])(ZZ)

(5) DistributedMultivariatePolynomial([x, y],Integer)

Type: Type
%%% (6) -> Xn(['x,'y])(QQ)

(6)
Record
aa:DistributedMultivariatePolynomial([x, y],Fraction(Integer))
,
bb:String

Type: Type
%%% (7) -> C ==> QQ

Type: Void
%%% (8) -> Xn(['x,'y])(QQ)

(8)
Record
aa:DistributedMultivariatePolynomial([x, y],Fraction(Integer))
,
bb:String

Type: Type
%%% (9) -> Xn(['x,'y])(C)

(9)
Record
aa:DistributedMultivariatePolynomial([x, y],Fraction(Integer))
,
bb:String

Type: Type
%%% (10) -> Xn(['x,'y])(ZZ)

(10)
Record(aa:DistributedMultivariatePolynomial([x, y],Integer),bb:String)

Type: Type
%%% (11) -> CC ==> QQ

Type: Void
%%% (12) -> Xn(['x,'y])(QQ)

>> System error:
The function BOOT::|%pform| is undefined.

%%% (12) -> Xn(['x,'y])(CC)

>> System error:
The function BOOT::|%pform| is undefined.

%%% (12) -> Xn(['x,'y])(ZZ)

>> System error:
The function BOOT::|%pform| is undefined.

%%% (12) -> CC ==> ZZ

Type: Void
%%% (13) -> Xn(['x,'y])(QQ)

(13)
Record
aa:DistributedMultivariatePolynomial([x, y],Fraction(Integer))
,
bb:String

Type: Type
%%% (14) -> Xn(['x,'y])(CC)

(14)
Record(aa:DistributedMultivariatePolynomial([x, y],Integer),bb:String)

Type: Type
%%% (15) -> Xn(['x,'y])(ZZ)

(15)
Record(aa:DistributedMultivariatePolynomial([x, y],Integer),bb:String)

Type: Type

Waldek Hebisch

unread,
Feb 13, 2026, 11:18:18 PM (10 days ago) Feb 13
to 'Ralf Hemmecke' via FriCAS - computer algebra system
On Fri, Feb 13, 2026 at 11:53:11PM +0100, 'Ralf Hemmecke' via FriCAS - computer algebra system wrote:
> Hi Waldek,
>
> This is a follow-up to
>
> https://groups.google.com/g/fricas-devel/c/uiXRlAq7-aI/m/VOP8UVCFDgAJ
>
> It is of similar type (macro function, i.e. macro that returns a macro), but
> yields an error just in the interpreter and only under very specific
> instances.

Yes, this is known problem. You reported is some time ago.

> That looks like a bug that is hopefully easy to fix.

No so easy. The best fix would be entirely new implementation of
macro expansion, but interpreter depends on current way, so that is
really replacing whole interpreter (or at least serious change to
the interpreter).

One could try local hacks, but it is not clear if such hack would
work. Also, there is possibility of undesirable interaction with
other parts of macro expansion.

> I first thought it was connected to using C in the definition of (3), but
> then I switched to CC and the problem
>
> The function BOOT::|%pform| is undefined.
>
> still appears, but only if CC expands to a type with a parameter like
> "Fraction(ZZ)".
>
> It would make me very happy if this problem can be fixed. I need the
> situation where CC is QQ.
>
> Anyhow, looking at the session below somehow seems to tell me that CC in (3)
> is *not* a local parameter whose name is irrelevant.

Yes, parameter name of "toplevel" macro is irrelevant, but parmanter name
of "inner" macro is relevant and subject to capture.

> OK, maybe in (3) I should just use C_unused_elsewhere_in_the_program instead
> of CC and be happy.

I do not understand why you do not simply write:

ZZ := Integer
QQ := Fraction(ZZ)
An(vl, CC) ==> DistributedMultivariatePolynomial(vl, CC)
Xn(vl, CC) ==> Record(aa: An(vl, CC), bb: String)

CC := QQ
Xn(['x,'y], QQ)

AFAICS, both using assignment to CC and changing form of definition of
'An' and 'Xn' alone is enough to avoid the problem.

> Nevertheless, I consider the problem as a bug.

As I wrote, this kind of macros not always works.

--
Waldek Hebisch

Ralf Hemmecke

unread,
Feb 14, 2026, 5:14:39 AM (10 days ago) Feb 14
to fricas...@googlegroups.com
On 2/14/26 05:18, Waldek Hebisch wrote:

> No so easy. The best fix would be entirely new implementation of
> macro expansion, but interpreter depends on current way, so that is
> really replacing whole interpreter (or at least serious change to
> the interpreter).
>
> One could try local hacks, but it is not clear if such hack would
> work. Also, there is possibility of undesirable interaction with
> other parts of macro expansion.

Do you have a hint for me how I can start looking myself into this macro
expansion issue. I currently have no idea of how I can see what actually
happens. If I had a function name that I hand over a string (with macro
definitons and applications) that I could start looking into, it would
probably motivate me to look a bit deeper.

> I do not understand why you do not simply write:
>
> ZZ := Integer
> QQ := Fraction(ZZ)
> An(vl, CC) ==> DistributedMultivariatePolynomial(vl, CC)
> Xn(vl, CC) ==> Record(aa: An(vl, CC), bb: String)
>
> CC := QQ
> Xn(['x,'y], QQ)

I have more macro expansion. The domains/packages can be combined in
different ways and the coefficient domain C is a parameter to all. I did
not want to repeat C, so I "invented" this macro scheme. That works fine
so far. But X has to be a macro (or domain) with only one parameter.
Unfortunately, in real life, as demonstrated above, it depends on 2,
namely vl and C.

QAB ==> QEtaAlgebraBasis
QRED(C,X) ==> QEtaReduction(C, X C, QAB X C)
QTOPRED(C,X) ==> QEtaTopReduction(C, X C, QAB X C)
QCOMP(C,X,RED) ==> QEtaComputation(C, X C, QAB X C, RED(C,X))

And eventually I want to use this package:

QCOMP(C, Xn(vl), QTOPRED)

That is why I need Xn(vl)(C).

BTW, it there a particular advantage of using

CC := QQ

instead of

CC ==> QQ

?

Ralf

Waldek Hebisch

unread,
Feb 14, 2026, 7:06:10 AM (9 days ago) Feb 14
to 'Ralf Hemmecke' via FriCAS - computer algebra system
On Sat, Feb 14, 2026 at 11:14:34AM +0100, 'Ralf Hemmecke' via FriCAS - computer algebra system wrote:
> On 2/14/26 05:18, Waldek Hebisch wrote:
>
> > No so easy. The best fix would be entirely new implementation of
> > macro expansion, but interpreter depends on current way, so that is
> > really replacing whole interpreter (or at least serious change to
> > the interpreter).
> >
> > One could try local hacks, but it is not clear if such hack would
> > work. Also, there is possibility of undesirable interaction with
> > other parts of macro expansion.
>
> Do you have a hint for me how I can start looking myself into this macro
> expansion issue. I currently have no idea of how I can see what actually
> happens. If I had a function name that I hand over a string (with macro
> definitons and applications) that I could start looking into, it would
> probably motivate me to look a bit deeper.

Well:

)set break break

Then error will bring you into Lisp debugger and something like

backtrace 20

will show you active function calls and their arguments.

Involved function are: 'macMacro', 'mac0Define', 'macApplication',
'macExpand', 'pfMapParts', 'mac0MLambdaApply', 'mac0ExpandBody'.
All in 'macex.boot'.

> > I do not understand why you do not simply write:
> >
> > ZZ := Integer
> > QQ := Fraction(ZZ)
> > An(vl, CC) ==> DistributedMultivariatePolynomial(vl, CC)
> > Xn(vl, CC) ==> Record(aa: An(vl, CC), bb: String)
> >
> > CC := QQ
> > Xn(['x,'y], QQ)
>
> I have more macro expansion. The domains/packages can be combined in
> different ways and the coefficient domain C is a parameter to all. I did not
> want to repeat C, so I "invented" this macro scheme. That works fine so far.
> But X has to be a macro (or domain) with only one parameter. Unfortunately,
> in real life, as demonstrated above, it depends on 2, namely vl and C.
>
> QAB ==> QEtaAlgebraBasis
> QRED(C,X) ==> QEtaReduction(C, X C, QAB X C)
> QTOPRED(C,X) ==> QEtaTopReduction(C, X C, QAB X C)
> QCOMP(C,X,RED) ==> QEtaComputation(C, X C, QAB X C, RED(C,X))
>
> And eventually I want to use this package:
>
> QCOMP(C, Xn(vl), QTOPRED)
>
> That is why I need Xn(vl)(C).

I see.

> BTW, it there a particular advantage of using
>
> CC := QQ
>
> instead of
>
> CC ==> QQ

Simpler semantics. Macro is expanded on all uses and may give
different value (or error) on each use. Assignment to variable
is done once and value will not change (unless there is another
assignment to the same variable).

Basically the only advantage of macros (and that is why they are
used in Spad) is that Spad compiler will use value of macro while
usually it has to assume that variables may change. This advantage
does not hold in the interpreter: interpreter tracks assignments
and will recompile a function when values of variables on which
it depends changes.

--
Waldek Hebisch
Reply all
Reply to author
Forward
0 new messages