gap-system / gap

Main development repository for GAP - Groups, Algorithms, Programming, a System for Computational Discrete Algebra
https://www.gap-system.org
GNU General Public License v2.0
813 stars 161 forks source link

Add a convenient way to display bindings inside of closures #3569

Open fingolfin opened 5 years ago

fingolfin commented 5 years ago

Consider this:

gap> f := ApplicableMethod( OrbitsDomain, [ SymmetricGroup(5), [1..5] ] );;
gap> Display(f);
function ( G, D )
    if D = MovedPoints( G ) then
        return op( G );
    else
        TryNextMethod();
    fi;
    return;
end

Quick, what is "op" standing for here?...

Well, of course again for OrbitsDomain.

Code that generates code by using closures can be super helpful and nice, but a nighmare to debug, largely because it can be so tricky to figure out the bindings inside a closure. A more direct example would be this:

gap> makeFun:=n -> x -> x + n;;
gap> f:=makeFun(42);;
gap> Display(f);
function ( x )
    return x + n;
end

If you hadn't seen the definition of f here, you'd have no way to know what n is. This should be different.


Note by @wilfwilson: Since creating this PR, @fingolfin has added the function BindingsOfClosure, which allows one to see and to access these variables in PR #3606. Thus we now get:

gap> BindingsOfClosure(f);
rec( n := 42 )

Note that this function is undocumented, so it might be nice to have some kind of documentation for it if someone feels like it while addressing this issue. End of note by @wilfwilson.


I suggest that we use BindingsOfClosure to add a way to view the local bindings of a function; at least the ones that are actually used in it. We could then show it when "Displaying" the function.

gap> Display(f);
# function closure with bindings:
#   n = 42
function ( x )
    return x + n;
end

Of course the "bindings" are strictly speaking all locals (including arguments) of the parent function of the given function (and this can potentially go back recursively). Coming back to the initial OrbitsDomain example, the parent function is OrbitsishOperation and it starts like this:

BindGlobal( "OrbitsishOperation", function( name, reqs, usetype, NewAorP )
    local op;

    # Create the attribute or property.
    op:= NewAorP( name, IsExternalSet );
...

So the "bindings" in my initial example actually also include name, reqs, usetype and NewAorP. But I think that's usually actually quite helpful.

wilfwilson commented 5 years ago

I updated the issue in light of #3606 being created and merged.

fingolfin commented 2 months ago

Here is an idea for perhaps making this more convenient: in my initial example, wouldn't it be nice if I could just write f.op to get the value of op?

It shouldn't be hard to implement that with a variation of the code in PR #3606

wilfwilson commented 2 months ago

That sounds like a nice idea.