Logtalk: meta::map, лямбда-выражение и доступ к приватному методу
Я думаю, что это проблема, связанная с объемом. Если у меня есть правило для моего объекта, как это:
:- public(new/2).
:- mode(new(+list, -object_identifier), one).
new(Args, Instance) :-
self(Self),
create_object(Instance, [instantiates(Self)], [], []),
Instance::process_arguments(Args).
Я считаю, что это прекрасно работает, если я делаю этот танец:
:- object(name, instantiates(name)).
Я не совсем понимаю, почему это необходимо, но я подозреваю, что это связано с моей реальной проблемой, которая заключается в том, что, если у меня есть стандартный цикл Prolog в моем объекте, вот так:
process_arguments([Arg|Args]) :- process_arg(Arg), process_arguments(Args).
process_arguments([]).
process_arg(Arg) :- ::asserta(something(Arg)).
Я нахожу это использование ::asserta
помещает факты в правильное пространство имен (на вновь созданный экземпляр). Тем не менее, если я остроумный и заменить тело process_arguments/1
с этим лямбда-выражением:
process_arguments(Args) :- meta::map([Arg]>>process_arg(Arg), Args).
затем я получаю, что мои факты добавляются в родительский класс и передаются всем экземплярам. Если я заменю это на это:
process_arguments(Args) :-
self(Self),
meta::map([Arg]>>(Self::process_arg(Arg)), Args).
тогда это работает, но я должен сделать process_arg/1
публичное правило, когда я предпочел бы нет. Что мне не хватает?
1 ответ
Позвольте мне начать с вашего фрагмента кода выше, где объект name
создает себя. Делая это, вы делаете name
свой собственный класс. В этом нет ничего плохого. В языках, которые поддерживают мета-классы, таких как Smalltalk и Logtalk, создание класса своим собственным мета-классом является классическим способом избежать бесконечной регрессии. См., Например, статью Википедии о мета-классах ( http://en.wikipedia.org/wiki/Metaclass). Смотрите также пример "отражения" в дистрибутиве Logtalk. Делая объект name
Сам по себе экземпляр, он играет как роль экземпляра (как он создает экземпляр объекта), так и роль класса (как он создается объектом). Если вы определили name
как отдельный объект, то есть объект, не имеющий отношения к другим объектам, он будет скомпилирован как прототип.
Теперь к вашему вопросу. В Logtalk мета-предикаты (такие как meta::map/2
) вызываются в контексте отправителя. Если process_arguments/1
предикат определен в name
тогда контекст выполнения (включая значение self) будет name
, Таким образом, пункты для something/1
будет утверждено в name
, Ваш обходной путь (с помощью встроенного метода self/1
) работает как положено, но заставляет вас заявлять process_arg/1
публичный предикат. Это ошибка в стабильной версии Logtalk, так как она должна также работать, объявив process_arg/1
предикат защищенный или частный (как отправитель name
и предикат объявляется в отправителе). Например:
:- object(name,
instantiates(name)).
:- public(new/2).
:- mode(new(+list, -object_identifier), one).
new(Args, Instance) :-
self(Self),
create_object(Instance, [instantiates(Self)], [set_logtalk_flag(dynamic_declarations, allow)], []),
meta::map({Instance}/[Arg]>>(Instance::process_arg(Arg)), Args).
:- private(process_arg/1).
process_arg(Arg) :-
::asserta(something(Arg)).
:- end_object.
Я добавлю исправление ошибки в общедоступную версию разработки Logtalk позже на этой неделе. Спасибо за внимание к этой ошибке.