Yall lambdas in asserted clauses

Hi!

I understand that yall lambdas expand into an efficient form when a Prolog file is consulted, but how about when clauses are asserted at runtime? How can I achieve the same kind of performance with asserted clauses. Unless they are already using the same mechanism, is there a way to trigger the same kind of rewriting / expansion before a clause with an yall lambda in it is asserted?

Best regards,

Patrick

In the doc page on [SWI-Prolog -- library(yall): Lambda expressions] maybe the first comment by LogicalCaptain (the one titled IMPORTANT x 3) answers your question.

In short, you should use these modules (in the order given, I think):

:- use_module(library(yall)).
:- use_module(library(apply)).
:- use_module(library(apply_macros)).

or do the equivalent as a query before you consult the code.

No. This is for consulted files, my question is about asserted clauses.

Lambdas in them seem very slow so these rewrites do not seem to be applied, so I wonder how one can apply them without writing to a file and consulting them.

Best regards,

Patrick

Hm, I was thinking that manually calling yall:expand_lambda/2 and asserting the expansion would do it, but I can’t seem to make that work. Presumably that’s where you’d want to look though.

AFAIK, term/goal expansion applies to any read term/goal, whether read from a file or from the command line: in the latter case, the very call to assert*, together with its arguments, would be expanded (i.e. before getting actually executed).

Are you positive (have you tested) that using those modules does not do the trick?

1 Like

True. Only call/1 and assert/1 do not do goal/term expansion. So, to assert as if read from a file, use expand_term/2 and assert the result. Note that bot the input and output can be lists.

For library(yall) there is a problem though. The expansion rule says

system:goal_expansion(Goal, Head) :-
    lambda_like(Goal),
    prolog_load_context(source, _),
    \+ current_prolog_flag(xref, true),
    expand_lambda(Goal, Head).

I.e., we demand that we are reading source code. That has to do with the fact that a lambda goal is expanded to a goal to an auxiliary predicate and this predicate is compiled using compile_aux_clauses/1, which demands a “source” context. The problem is that expand_goal/2 has no way to tell the caller it wants the goal to be expanded some way and specify a helper predicate.

For short, you cannot get this done for library(yall) :frowning: Unless you want to refactor library(yall) a bit to make it possible to call expand_lambda/2 outside a source context and deal with the result. That is doable, but it might be easier to define the required helper by hand and call this without using yall lambdas …

2 Likes

@jp-diegidio Yes, term/goal expansion does not apply to assert.

@jan No worries, in the meanwhile we have our own lambda library that also works for asserted clauses and gives the equal speed as explicitly writing helper predicates does. Maybe we will port it back to yall, but from the discussion it seems yall is not engineered well so probably should be replaced anyways.

Best regards,

Patrick

Not sure what you mean. The library has an IMO serious problem that the semantics of the interpreted and compiled versions is not the same. That is a fundamental issue, although SWI-Prolog’s current expansion code could detect that the code is subject to this issue (and thus warn).

I’m still not a big fan of lambdas in Prolog … Seems you have a different experience :slight_smile: I’m surely interested if you have a better version.

Thanks. Out of interest: is there a reason why that is so? (Could there be a Prolog that expands everything?)

Note that it’s always safe to call expand_term/2 even if it’s already been called because term exapansion repeatedly calls term_expansion/2 and goal_expansion/2 until it reaches a fixed-point.

(Hmmm … @jan – is this correct? The documentation says that expand_goal/2 repeatedly calls goal_expansion/2 until there’s no change but term_expansion/2 seems to only call expand_goal/2 once (and then calls the DCG expansion).

Does not apply to assert and call, as Jan was saying.

Do you or anybody happen to know why that is so?

Performance. It is barely every needed and if you need it, just call expand_goal/2 and call the result. I think SICStus does so by default. Given user hooks, expand_goal/2 can be arbitrary slow though.

goal_expansion/2 is called by expand_goal/2 until fixed point is reached. expand_term/2 calls term_expansion only once. That is AFAIK how it traditionally works. Otherwise there are a lot of variations in how Prolog systems handle the expansion. Would be great if we can synchronize that!

2 Likes