Sharing term expansion across modules

this is so great :slight_smile:

thanks!

The inline works nicely —

It seems that the inline directive has to sit on the client side – i.e. for each client i have to indicate which calls will be inlined.

I wasn’t yet able to get it to work across clients by putting the inline directive into the module that does the expansions.

I think it would be nice if i could do that as well – so i can centralize inlining as well.

Do you understand why the compiler doesn’t do it … in theory the compiler should see the inline directive in the called “lower level” module first.

Edit:

Perhaps it has to do with the module prefix.

every caller sits in its own module – so the:

:- inline(functor/2)

would need to be qualified – but generally, somehow:

:-inline(_:functor/2)

to mean any module prefix should be recognized – i guess,

Right, so when :-inline foo/1. is put inside of module m1, and then module m2, a user of module m1, calls foo/1, then foo/1 does not get expanded within module m2.

Only if the inline directive is placed in module m2 – the user of module m1, then the expansion occurs in module m2.

Or at least, that’s the case in my code.

ok – i missed that.

thanks,

Dan

My bad …

I forgot to change the debug value from false to true :slight_smile:

To ensure that the expansion succeeds during debug.

oh well,

I now moved the code into a dedicated module, and tried to “use” it, but now, it doesn’t work anymore.

My feeling is that once inline is in a module then either fuctor or clause and/or is_inlined, doesn’t find the goal defined in another module and then called from yet another module.

I will put the inline code again into the module that defines the goals that should be inlined via expansions.

Edit:

Now it stopped working alltogether – i must have introduced some bug somewhere – am searching …

thanks.

When i add an assertz such as so, nothing gets asserted, during debug –

	goal_expansion(G, W) :- 
	    current_prolog_flag(debug, true), 
	    functor(G, F, A),
	    is_inlined(F, A),
	    clause(G, W), 
	    assertz(expanded(G,W)).

yes, indeed – i put it to true, when things didn’t work thinking it means that debug must be running, then things worked – then not anymore – and now i put it back to false. and now it works again …

perhaps something was cached somewhere … dont now

i think this inline directive is incredible – i have to say that again – it can be used in all kind of places to help prolog speed itself up while also keeping software engineering principles, such as encapsulation and modularity, intact.

I didn’t keep them – but, i had an across the board slow down after i created more modularity – which added one indirection step to my core backend.

I guess, once I complete the “refactoring” into using such explansions I will be able to measure them again.

As a general practice i qualify all my predicates in calls, so perhaps this practice allows calling predicates in m1 which are used from module m0, in module m2 – otherwise I would, i guess, need to use m0 in m2.

I started this practice after I tried out Logtalk and i liked the idea of having qualified predicate names.

True, but i do keep to modularization – so i qualify predicates but also use the module they came from – so, i don’t break the interface principle.

Its mainly for documentation purpose – otherwise i just don’t know anymore what the source of all those predicates are … and predicate names are global in any case.

That is very interesting and surprising.

I only (hopefully) call goals that were exported, so in practice the qualification is not needed.

This is also what i meant with “global” - my understanding is that all predicates that are exported from a module are in a global name space – so i can’t have two with the same name predicates exported, each from a different module.

If qualification reduces performance – then perhaps best if i switch to a prefix naming convention per module – that’s quite a hassle though …

Dan

but try to export foo from each module … let me give it a try.

Edit:

I got this error when i tried to export foo from both m1 and m2 –

ERROR: [Thread pdt_console_client_0_Default Process] import/1: No permission to import m2:foo/0 into user (already imported from m1)

But, it seems that with except you ensured that foo/1 is not exported into user and then you still access it via the fully qualified name …

So, it amounts to the same, i think …

Although, its really unclear – that by default everything gets imported to user during compile time, all the while i intend to import into another module and not user.

ah, ok …

But, still, in my example, merely compile imported both into user — perhaps its an eclipse PDT command line thing …

Yes, i know. You have to be disciplined in swi prolog – and its too bad – since software engineering is important …

I am still reeling from the fact that qualified names are compiled differently – i don’t see the reason why this is so … they should be “binary” compatible with non qualified calling of exported goals/predicates.

I cloned a piece of code – the first one with qualified predicate calls and the second one without – i also changed the name of the head, so these are two different predicates with the exact same body sans qualification of goal terms.

And, there are a few difference in the VMIs generated

Interestingly, the non-qualified version generates more code … and there is a kind of label included – a jump of kind, or reference to var on stack?

Also, the fully qualified name is also included as an extra parameter in calls … so internally, there is perhaps a bit more processing or checking occuring?

Which of the two would run faster?

@jan , could you perhaps shed a bit light on this … which version is preferred a qualified one or a non-qualified – or it depends on something …

Dan

?- vm_list(pred1_/4).
========================================================================
m1:pred1_/4
========================================================================
       0 s_virgin
       1 i_exit
----------------------------------------
clause 1 (<clause>(000000000775A5E0)):
----------------------------------------
       0 i_enter
       1 b_var0
       2 b_var1
       3 b_var(3)
       5 i_callm(q1,q1:pred21/3)
       8 b_var(3)
      10 b_var2
      11 i_callm(q1,q1:pred22/2)
      14 b_var0
      15 b_atom(atom1)
      17 b_firstvar(4)
      19 i_callm(q1,q1:pred23/3)
      22 b_var(4)
      24 b_var2
      25 i_departm(q1,q1:pred24/2)
      28 i_exit
true.

?- vm_list(pred1_2/4).
========================================================================
m1:pred_2/4
========================================================================
       0 s_virgin
       1 i_exit
----------------------------------------
clause 1 (<clause>(00000000080B0A50)):
----------------------------------------
       0 i_enter
       1 b_var0
       2 b_var1
       3 b_var(3)
       5 i_call(q1:pred21/3)
       7 b_var(3)
       9 b_var2
      10 i_call(q1:pred22/2)
      12 b_var0
      13 b_atom(atom1)
      15 b_firstvar(4)
      17 i_call(q1:pred23/3)
      19 l_nolco('L1')
      21 l_var(0,4)
      24 l_var(1,2)
      27 i_lcall(q1:pred24/2)
L1:   29 b_var(4)
      31 b_var2
      32 i_depart(q1:pred24/2)
      34 i_exit

There are pros and cons to this. I don’t want to start a side-discussion about this … probably everything that could be said has already been said in the Python community (Python also doesn’t enforce access to “private” functions, but relies on naming convenetions). FWIW, I’ve found SWI-Prolog’s approach convenient when debugging; and I haven’t had problems with swipl’s “liberal” rules (nor with Python’s; nor with Java or C++, for that matter).

For a tongue-in-cheek opinion on this: bondage-and-discipline language