I think this is a scope related problem. If I have a rule on my object like this:
:- public(new/2).
:- mode(new(+list, -object_identifier), one).
new(Args, Instance) :-
self(Self),
create_object(Instance, [instantiates(Self)], [], []),
Instance::process_arguments(Args).
I find this works fine if I do this dance:
:- object(name, instantiates(name)).
I don’t fully understand why this is necessary but I suspect it is linked to my actual problem, which is that if I have your standard Prolog loop in my object, like so:
process_arguments([Arg|Args]) :- process_arg(Arg), process_arguments(Args).
process_arguments([]).
process_arg(Arg) :- ::asserta(something(Arg)).
I find this use of ::asserta puts the facts in the right namespace (on the newly-created instance). However, if I get witty and replace the body of process_arguments/1 with this lambda expression:
process_arguments(Args) :- meta::map([Arg]>>process_arg(Arg), Args).
then I wind up with my facts being added to the parent class and shared by all the instances. If I replace it with this:
process_arguments(Args) :-
self(Self),
meta::map([Arg]>>(Self::process_arg(Arg)), Args).
then it works, but I have to make process_arg/1 a public rule when I’d rather not. What am I missing?
Let me start first with your snippet of code above where the object
nameinstantiates itself. In doing this, you makenameits own class. Nothing wrong where. In languages that support meta-classes, such as Smalltalk and Logtalk, making a class its own meta-class is a classical way of avoiding an infinite regression. See, for example the Wikipedia entry on meta-classes (http://en.wikipedia.org/wiki/Metaclass). See also the “reflection” example in the Logtalk distribution. By making the objectnameinstantiate itself, it plays both the role of an instance (as it instantiates an object) and the role of a class (as it’s instantiated by an object). If you definednameas a stand-alone object, i.e. an object with no relation to other objects, it would be compiled as a prototype.Now to your question. In Logtalk, meta-predicates (such as
meta::map/2) are called in the context of the sender. If theprocess_arguments/1predicate is defined inname, then the execution context (including the value of self) will bename. Thus, the clauses forsomething/1will be asserted inname. Your workaround (by using the built-in methodself/1) works as expected but it does forces you to declareprocess_arg/1public predicate. This is a bug in the stable Logtalk version as it should also work by declaring theprocess_arg/1predicate protected or private (as the sender isnameand the predicate is declared in the sender). For example:I will push the bug fix into the publicly available Logtalk development version later this week. Thanks for driving my attention to this bug.