В информатике денотатационная семантика (первоначально известная как математическая семантика или семантика Скотта-Стрейчи ) — это подход к формализации значений языков программирования путем построения математических объектов (называемых денотациями ), которые описывают значения выражений из языков. Другие подходы, обеспечивающие формальную семантику языков программирования, включают аксиоматическую семантику и операционную семантику .
В широком смысле денотационная семантика занимается поиском математических объектов, называемых доменами , которые представляют то, что делают программы. Например, программы (или программные фразы) могут быть представлены частичными функциями [1] [2] или играми [3] между средой и системой.
Важным принципом денотационной семантики является то, что семантика должна быть композиционной : денотат программной фразы должен строиться из денотатов ее подфраз .
Денотационная семантика возникла в работах Кристофера Стрейчи и Даны Скотт , опубликованных в начале 1970-х годов. [1] [2] Первоначально разработанная Стрейчи и Скоттом, денотатационная семантика определяла значение компьютерной программы как функции , которая отображает входные данные в выходные данные. [2] Чтобы придать смысл рекурсивно определенным программам, Скотт предложил работать с непрерывными функциями между областями , в частности с полными частичными порядками . Как описано ниже, продолжалась работа по исследованию подходящей денотационной семантики для таких аспектов языков программирования, как последовательность, параллелизм, недетерминизм и локальное состояние .
Денотационная семантика была разработана для современных языков программирования, которые используют такие возможности, как параллелизм и исключения , например, Concurrent ML , [4] CSP , [5] и Haskell . [6] Семантика этих языков композиционная, поскольку значение фразы зависит от значений ее подфраз. Например, значение аппликативного выражения f(E1,E2) определяется с точки зрения семантики его подфраз f, E1 и E2. В современном языке программирования E1 и E2 могут оцениваться одновременно, и выполнение одного из них может повлиять на другой, взаимодействуя через общие объекты , в результате чего их значения будут определяться друг с другом. Кроме того, E1 или E2 могут вызвать исключение, которое может прервать выполнение другого. В разделах ниже описываются особые случаи семантики этих современных языков программирования.
Денотационная семантика приписывается программной фразе как функция от среды (содержащей текущие значения ее свободных переменных) до ее обозначения. Например, фраза n*m
создает обозначение, если ей предоставляется среда, в которой есть привязка для двух ее свободных переменных: n
и m
. Если в среде n
имеет значение 3 и m
имеет значение 5, то обозначение равно 15. [2]
Функцию можно представить как набор упорядоченных пар аргументов и соответствующих значений результатов. Например, набор {(0,1), (4,3)} обозначает функцию с результатом 1 для аргумента 0, результатом 3 для аргумента 4 и неопределенным в противном случае.
Рассмотрим, например , функцию факториала , которую можно рекурсивно определить как:
int факториал ( int n ) { if ( n == 0 ) то вернуть 1 ; иначе вернуть n * факториал ( n -1 ); }
Чтобы придать смысл этому рекурсивному определению, обозначение построено как предел приближений, где каждое приближение ограничивает количество вызовов факториала. Вначале мы начинаем без вызовов — следовательно, ничего не определено. В следующем приближении мы можем добавить упорядоченную пару (0,1), поскольку для этого не требуется повторно вызывать факториал. Аналогичным образом мы можем добавить (1,1), (2,2) и т. д., добавляя по одной паре в каждом последовательном приближении, поскольку для вычисления факториала (n) требуется n+1 вызовов. В пределе мы получаем полную функцию от до, определенную всюду в своей области определения.
Формально мы моделируем каждое приближение как частичную функцию . Наше приближение затем многократно применяет функцию, реализующую «сделать более определенную частичную функцию факториала», т. е. начиная с пустой функции (пустого множества). F можно определить в коде следующим образом (используя for ):Map<int,int>
intfactorial_nonrecursive ( Map < int , int > factorial_less_defined , int n ) { if ( n == 0 ) then return 1 ; иначе if ( fprev = поиск ( factorial_less_defined , n -1 )) тогда верните n * fprev ; иначе верните NOT_DEFINED ; } Map < int , int > F ( Map < int , int > factorial_less_defined ) { Map < int , int > new_factorial = Map . пустой (); for ( int n in all < int > ()) { if ( f = Factorial_nonrecursive ( factorial_less_define , n ) != NOT_DEFINED ) new_factorial . положить ( н , ж ); } Вернуть новый_факториал ; }
Затем мы можем ввести обозначение Fn , чтобы указать, что F применяется n раз .
Этот итерационный процесс строит последовательность частичных функций от до . Частичные функции образуют полный по цепочке частичный порядок, используя ⊆ в качестве порядка. Более того, этот итерационный процесс лучших приближений факториальной функции образует расширяющее (также называемое прогрессивным) отображение, поскольку каждое из них использует ⊆ в качестве порядка. Таким образом, согласно теореме о неподвижной точке (в частности, теореме Бурбаки–Витта ) для этого итерационного процесса существует неподвижная точка.
В данном случае неподвижной точкой является наименьшая верхняя граница этой цепочки, представляющая собой полную factorial
функцию, которую можно выразить как объединение
Найденная нами фиксированная точка является наименее фиксированной точкой F , поскольку наша итерация началась с наименьшего элемента в области определения (пустого набора). Чтобы доказать это, нам понадобится более сложная теорема о неподвижной точке, такая как теорема Кнастера – Тарского .
Концепция доменов мощности была разработана для придания денотационной семантики недетерминированным последовательным программам. Записывая P для конструктора степенной области, область P ( D ) является областью недетерминированных вычислений типа, обозначенного D .
Существуют трудности со справедливостью и неограниченностью в теоретико-предметных моделях недетерминизма. [7]
Многие исследователи утверждают, что теоретико-предметные модели, приведенные выше, недостаточны для более общего случая параллельных вычислений . По этой причине были представлены различные новые модели . В начале 1980-х годов люди начали использовать стиль денотационной семантики для определения семантики параллельных языков. Примеры включают работу Уилла Клингера с моделью актера ; работа Глинна Винскеля со структурами событий и сетями Петри ; [8] и работу Франсеса, Хоара, Лемана и де Ровера (1979) по семантике трассировки для CSP. [9] Все эти направления исследований остаются в стадии исследования (см., например, различные денотационные модели для CSP [5] ).
Недавно Винскель и другие предложили категорию профункторов в качестве теории предметной области параллелизма. [10] [11]
Состояние (например, куча) и простые императивные функции можно напрямую смоделировать с помощью денотационной семантики, описанной выше. Основная идея состоит в том, чтобы рассматривать команду как частичную функцию в некоторой области состояний. Значение " x:=3
" - это функция, которая переводит состояние в состояние с 3
присвоенным значением x
. Оператор последовательности " ;
" обозначается композицией функций. Затем конструкции с фиксированной точкой используются для придания семантики циклическим конструкциям, таким как " while
".
Все становится сложнее при моделировании программ с локальными переменными. Один из подходов состоит в том, чтобы больше не работать с доменами, а вместо этого интерпретировать типы как функторы из некоторой категории миров в категорию доменов. Программы тогда обозначаются естественными непрерывными функциями между этими функторами. [12] [13]
Многие языки программирования позволяют пользователям определять рекурсивные типы данных . Например, тип списков номеров можно указать с помощью
список типов данных = Минусы nat * list | Пустой
В этом разделе рассматриваются только функциональные структуры данных, которые не могут быть изменены. Обычные императивные языки программирования обычно позволяют изменять элементы такого рекурсивного списка.
Другой пример: тип обозначений нетипизированного лямбда -исчисления:
тип данных D = D из ( D → D )
Проблема решения уравнений предметной области связана с поиском областей, моделирующих такие типы данных. Грубо говоря, один из подходов состоит в том, чтобы рассматривать совокупность всех доменов как сам домен, а затем решать там рекурсивное определение.
Полиморфные типы данных — это типы данных, определенные с помощью параметра. Например, тип α list
s определяется выражением
тип данных α список = Минусы α * α список | Пустой
Таким образом, списки натуральных чисел имеют тип nat list
, а списки строк — тип string list
.
Некоторые исследователи разработали теоретико-доменные модели полиморфизма. Другие исследователи также моделировали параметрический полиморфизм в рамках конструктивных теорий множеств.
Недавняя область исследований связана с денотационной семантикой для языков программирования, основанных на объектах и классах. [14]
После развития языков программирования, основанных на линейной логике , денотационная семантика была передана языкам для линейного использования (см., например, сети доказательств , пространства когерентности ), а также полиномиальную временную сложность. [15]
Проблема полной абстракции для языка последовательного программирования PCF долгое время была большим открытым вопросом в денотационной семантике. Трудность с PCF заключается в том, что это очень последовательный язык. Например, в PCF невозможно определить функцию « параллельное-или» . Именно по этой причине подход с использованием доменов, представленный выше, дает денотационную семантику, которая не является полностью абстрактной.
Этот открытый вопрос был в основном решен в 1990-х годах с развитием семантики игр , а также с помощью методов, включающих логические отношения . [16] Более подробную информацию см. на странице PCF.
Часто бывает полезно перевести один язык программирования на другой. Например, язык параллельного программирования может быть переведен в исчисление процессов ; язык программирования высокого уровня может быть переведен в байт-код. (Действительно, традиционную денотационную семантику можно рассматривать как интерпретацию языков программирования во внутренний язык категории предметных областей.)
В этом контексте понятия денотационной семантики, такие как полная абстракция, помогают удовлетворить проблемы безопасности. [17] [18]
Часто считается важным связать денотационную семантику с операционной семантикой . Это особенно важно, когда денотационная семантика скорее математическая и абстрактная, а операционная семантика более конкретна или близка к вычислительной интуиции. Часто представляют интерес следующие свойства денотационной семантики.
Для семантики в традиционном стиле адекватность и полную абстракцию можно понимать примерно как требование, чтобы «операционная эквивалентность совпадала с денотационным равенством». Для денотационной семантики в более интенсиональных моделях, таких как модель актера и исчисление процессов , внутри каждой модели существуют разные понятия эквивалентности, поэтому концепции адекватности и полной абстракции являются предметом споров, и их труднее определить. Также математическая структура операционной семантики и денотационной семантики может стать очень близкой.
Дополнительные желательные свойства, которые мы можем захотеть сохранить между операционной и денотационной семантикой:
Важным аспектом денотационной семантики языков программирования является композиционность, при которой денотат программы строится из обозначений ее частей. Например, рассмотрим выражение «7+4». Композиционность в данном случае заключается в том, чтобы обеспечить значение «7 + 4» с точки зрения значений «7», «4» и «+».
Основная денотационная семантика в теории предметной области является композиционной, поскольку она задается следующим образом. Начнем с рассмотрения фрагментов программ, т.е. программ со свободными переменными. Контекст типизации присваивает тип каждой свободной переменной. Например, выражение ( x + y ) может рассматриваться в контексте типизации ( x : nat
, y : nat
). Теперь придадим денотационную семантику фрагментам программы, используя следующую схему.
nat
должно быть областью натуральных чисел: 〚nat
〛= ⊥ .nat
, y : nat
〛= ⊥ × ⊥ . В частном случае значение пустого контекста типизации без переменных — это домен с одним элементом, обозначаемым 1.nat
〛:1→ ⊥ — это функция, постоянно имеющая число «7», а 〚x : , y : ⊢ x + y : 〛: ⊥ × ⊥ → ⊥ — функция, которая складывает два числа.nat
nat
nat
Теперь смысл составного выражения (7+4) определяется путем составления трех функций 〚⊢7: nat
〛:1→ ⊥ , 〚⊢4: 〛:1→ ⊥ и 〚x : , y : ⊢ x + y : 〛: ⊥ × ⊥ → ⊥ .nat
nat
nat
nat
По сути, это общая схема композиционной денотационной семантики. Здесь нет ничего конкретного о доменах и непрерывных функциях. Вместо этого можно работать с другой категорией . Например, в семантике игр категория игр включает игры как объекты и стратегии как морфизмы: мы можем интерпретировать типы как игры, а программы как стратегии. Для простого языка без общей рекурсии мы можем обойтись категорией множеств и функций . Для языка с побочными эффектами мы можем работать с монадой в категории Клейсли . Для языка с состоянием мы можем работать в категории функторов . Милнер выступал за моделирование местоположения и взаимодействия, работая в категории, где интерфейсы являются объектами, а биграфы — морфизмами. [20]
По словам Даны Скотт (1980): [21]
По данным Клингера (1981): [22] : 79.
В некоторых работах по денотационной семантике типы интерпретируются как области в смысле теории доменов, которую можно рассматривать как ветвь теории моделей , что приводит к связям с теорией типов и теорией категорий . В информатике существуют связи с абстрактной интерпретацией , проверкой программ и проверкой моделей .
{{cite book}}
: CS1 maint: location missing publisher (link){{cite book}}
: |work=
игнорируется ( помощь )