Более безопасные тесты типа в Prolog
ISO-Prolog (ISO/IEC 13211-1:1995, включая Cor.1: 2007, Cor.2: 2012) предлагает следующее встроенные предикаты для тестирования типа термина:
8.3 Типовое тестирование
1 вар /1. 2 атом /1. 3 целое число /1. 4 поплавка /1. 5 атомных /1. 6 Соединение /1. 7 nonvar/1. 8 количество /1. 9 вызываемым /1. 10 земля /1. 11 acyclic_term/1.
Внутри этой группы есть те, чья цель состоит исключительно в том, чтобы протестировать для определенного экземпляра, то есть 8.3.1 var/1
, 8.3.7 nonvar/1
, 8.3.10 ground/1
, и те, которые предполагают, что член достаточно инстанцируется таким образом, что тест типа безопасен. К сожалению, они сочетаются с тестированием для конкретного экземпляра.
Рассмотрим цель integer(X)
, которая терпит неудачу, если X
- невариантный член, который не является целым числом и, когда X
является переменной. Это разрушает многие желательные декларативные свойства:
?- X = 1, integer(X).
true.
?- integer(X), X = 1.
false.
В идеале второй запрос будет либо успешным, используя какую-либо форму coroutining; или он выдаст ошибку создания 1 в соответствии с классификацией ошибок . В конце концов:
7.12.2 Классификация ошибок
Ошибки классифицируются в соответствии с формой Error_term:
a) При возникновении ошибки при создании аргумент или один из его компонентов - переменная, а также требуется экземпляр аргумента или компонента. Он имеет форма instantiation_error
.
...
Обратите внимание, что эта неявная комбинация тестирования экземпляров и тестирования типов приводит к многочисленным ошибкам в программах Prolog, а также здесь на SO.
Быстрое исправление этой ситуации заключалось бы в том, чтобы добавить явный тест перед каждым встроенным тестом, либо дословно, как
( var(T) -> throw(error(instantiation_error,_)) ; true),
integer(T), ....
или более компактно, так как
functor(T, _,_),
integer(T), ....
он может быть даже
T =.. _,
integer(T), ...
Мой вопрос двоякий:
Как обеспечить эту функциональность на уровне пользователя?
и, чтобы сделать это также немного сложным:
Какова самая компактная реализация более безопасного atomic/1
, записанного в ISO-Prolog?
1 Другими менее желательными параметрами могут быть петли или создать ошибку ресурса. Еще предпочтительнее неправильного результата.
Ответы
Ответ 1
Это очень наивная попытка реализовать оба предлагаемых вами решения.
Во-первых, has_type(Type, Var)
, который преуспевает или сбой при ошибке создания:
has_type(Type, X) :-
var(X), !,
throw(error(instantiation_error, _)).
has_type(Type, X) :-
nonvar_has_type(Type, X).
nonvar_has_type(atom, X) :- atom(X).
nonvar_has_type(integer, X) :- integer(X).
nonvar_has_type(compound, X) :- compound(X).
% etc
Во-вторых, a could_be(Type, Var)
(аналог must_be/2
), который использует сопроцессор, чтобы запрос мог быть успешным в какой-то момент в будущем:
could_be(Type, X) :-
var(X), !,
freeze_type(Type, X).
could_be(Type, X) :-
nonvar_has_type(Type, X).
freeze_type(integer, X) :- freeze(X, integer(X)).
freeze_type(atom, X) :- freeze(X, atom(X)).
freeze_type(compound, X) :- freeze(X, compound(X)).
% etc
Есть несколько слабых сторон этого подхода, но ваши комментарии могут помочь мне лучше понять варианты использования.
EDIT: "Типы" в Prolog
Типы в Prolog, как я их понимаю, не являются "типами": это просто информация, которая может быть запрошена во время выполнения, и которая существует, потому что это полезная нечеткая абстракция базовой реализации.
Единственный способ, которым я смог практично использовать "тип", - это "пометить" мои переменные, как в составных терминах number(1)
, number(pi)
, operator(+)
, date(2015, 1, 8)
и скоро. Затем я могу помещать переменные в них, писать детерминированные или полудетерминированные предикаты, понимать, что мой код означает, когда я увижу его через неделю....
Таким образом, свободная переменная и целое число являются просто терминами; главным образом потому, что, поскольку ваш вопрос очень остро указывает, свободная переменная может стать целым числом, или атомом, или составным термином. Вы можете использовать coroutining, чтобы удостовериться, что свободная переменная может только стать определенным "типом" термина позже, но это по-прежнему хуже с использованием составных терминов с практической точки зрения.
Очень вероятно, что я смешиваю здесь очень разные проблемы; и, честно говоря, мой опыт работы с Prolog в лучшем случае ограничен. Я просто прочитал документацию по реализации, которую я использую, и попытаюсь найти лучший способ использовать ее в моих интересах.
Ответ 2
Тестирование на типы должно отличать себя от традиционных встроенных модулей тестирования типов, которые неявно также проверяют на достаточный экземпляр. Таким образом, мы эффективно тестируем только тип (itype
). И если они недостаточно созданы, возникает соответствующая ошибка.
Для типа nn
, таким образом, существует предикат тестирования типа nn_itype/1
с единственным условием ошибки
a) Если существует & theta; и & sigma; такой, что nn_itype(Xθ)
true и nn_itype(Xσ)
- false
— instantiation_error
.
atom_itype(A) :-
functor(A, _, _), % for the instantiation error
atom(A).
integer_itype(I) :-
functor(I, _, _),
integer(I).
atomic_itype(AC) :-
functor(AC,_,0).
list_itype(L) :-
\+ \+ length(L, _), % for silent failure
sort(L, _). % for the instantiation error
В SWI из-за его различного поведения в length/2
используйте скорее:
list_itype(L) :-
'$skip_list'(_, L, T),
functor(T,_,_),
T == [].