However, I want it all to be lazy. That is, nothing should happen until foldl starts pulling items, and then the work should happen just in time. None of the intermediary lists nor the final list should ever be materialised.
How would you change the process predicate to make this sort of lazy processing possible? I am aware of lazy lists but it is not obvious to me how to convert process to use them.
Please assume process outputs items in the same order as the input (although that is not shown).
Last thing I need is to be able to chain things which do not return 1-for-1 lists. For example, say at the top I insert [1,2,3] and the next process creates [4,5,6,7,8,9] from that input. I dont see an obvious way to do that with lazy lists or otherwise.
I’ve got what i need working with lazy_list except its useless for the purpose since its about 20x slower than a fully materialized version. I had the same experience when using lazy_findall/3.
This way you show is slightly unusual. It is impossible to tell without a concrete example, of course, so I am only guessing; still, the usual way would be more like:
You should play around and see that reversing the order of the clauses makes no difference; and that in the specific case of char_code/2, removing the cut makes not difference. For a non-deterministic predicate “do_work” it will of course make a difference, you will get all possible combinations. Here is a silly example just to demonstrate:
About lazy evaluation. The Prolog alternative to lazy evaluation is backtracking. You can get very far with it. The huge advantage is that it is very fast and needs far less memory; you never need to materialize any lists. The huge disadvantage is that you lose solutions on backtracking. If you can provide a concrete example, it might be easier to judge if you need lists or lazy lists.
I assume you know that SWI Prolog lazy lists are implemented as constraints using attributed variables, and that unification of attributed variables is going to be orders of magnitude slower that unification that doesn’t involve attributed variables.
To make it concrete, the attr_unify_hook that gets invoked whenever a variable with a lazy_list attribute is unified:
Note that several of the predicates involving lazy lists have a chunk size argument or option. That reduces the number of wakeups. Still, there is quite a bit of work involved in extending the lazy list.
The lazy_list abstraction is practical. It’d be great if you could write something using a similar contract which can be used to pipe lists into processes and processes into each other via lists.
I’m not really sure how memoisation fits into all this, but I think its strange that a lazy list ends up exploding the memory due to overhead.