Введение
В вычислительной технике уникальный тип гарантирует, что объект используется в однопоточном режиме, с не более чем одной ссылкой на него. Если значение имеет уникальный тип, функция, применяемая к нему, может быть оптимизирована для обновления этого значения непосредственно в объектном коде. Такие обновления на месте повышают эффективность функциональных языков при сохранении референтной прозрачности. Уникальные типы также могут использоваться для интеграции функционального и императивного программирования.
Введение
Типирование уникальности лучше всего объяснить на примере. Рассмотрим функцию `readLine`, которая читает следующую строку текста из заданного файла:
function readLine(File f) returns String
return line where
String line = doImperativeReadLineSystemCall(f)
end
end
Now doImperativeReadLineSystemCall reads the next line from the file using an OS level system call which has the side effect of changing the current position in the file. But this violates referential transparency because calling it multiple times with the same argument will return different results each time as the current position in the file gets moved. This in turn makes readLine violate referential transparency because it calls doImperativeReadLineSystemCall. However, using uniqueness typing, we can construct a new version of readLine that is referentially transparent even though it's built on top of a function that's not referentially transparent:
function readLine2(unique File f) returns (unique File, String)
return (differentF, line) where
String line = doImperativeReadLineSystemCall(f)
File differentF = newFileFromExistingFile(f)
end
end
The unique declaration specifies that the type of f is unique; that is to say that f may never be referred to again by the caller of readLine2 after readLine2 returns, and this restriction is enforced by the type system. And since readLine2 does not return f itself but rather a new, different file object differentF, this means that it's impossible for readLine2 to be called with f as an argument ever again, thus preserving referential transparency while allowing for side effects to occur.
function `readLine`(File f) returns String
return line where
String line = doImperativeReadLineSystemCall(f)
end
end
function readLine(File f) returns String
return line where
String line = doImperativeReadLineSystemCall(f)
end
end
Now doImperativeReadLineSystemCall reads the next line from the file using an OS level system call which has the side effect of changing the current position in the file. But this violates referential transparency because calling it multiple times with the same argument will return different results each time as the current position in the file gets moved. This in turn makes readLine violate referential transparency because it calls doImperativeReadLineSystemCall. However, using uniqueness typing, we can construct a new version of readLine that is referentially transparent even though it's built on top of a function that's not referentially transparent:
function readLine2(unique File f) returns (unique File, String)
return (differentF, line) where
String line = doImperativeReadLineSystemCall(f)
File differentF = newFileFromExistingFile(f)
end
end
The unique declaration specifies that the type of f is unique; that is to say that f may never be referred to again by the caller of readLine2 after readLine2 returns, and this restriction is enforced by the type system. And since readLine2 does not return f itself but rather a new, different file object differentF, this means that it's impossible for readLine2 to be called with f as an argument ever again, thus preserving referential transparency while allowing for side effects to occur.
Теперь `doImperativeReadLineSystemCall` читает следующую строку из файла, используя системный вызов уровня ОС, который имеет побочный эффект – изменение текущей позиции в файле. Но это нарушает референтную прозрачность, поскольку многократный вызов этой функции с одним и тем же аргументом будет возвращать разные результаты, так как текущая позиция в файле будет изменяться. Это, в свою очередь, приводит к нарушению референтной прозрачности для `readLine`, поскольку она вызывает `doImperativeReadLineSystemCall`. Однако, используя типирование уникальности, мы можем создать новую версию `readLine`, которая будет референтно прозрачной, даже если она построена на основе функции, которая не является референтно прозрачной:
function readLine(File f) returns String
return line where
String line = doImperativeReadLineSystemCall(f)
end
end
Now doImperativeReadLineSystemCall reads the next line from the file using an OS level system call which has the side effect of changing the current position in the file. But this violates referential transparency because calling it multiple times with the same argument will return different results each time as the current position in the file gets moved. This in turn makes readLine violate referential transparency because it calls doImperativeReadLineSystemCall. However, using uniqueness typing, we can construct a new version of readLine that is referentially transparent even though it's built on top of a function that's not referentially transparent:
function readLine2(unique File f) returns (unique File, String)
return (differentF, line) where
String line = doImperativeReadLineSystemCall(f)
File differentF = newFileFromExistingFile(f)
end
end
The unique declaration specifies that the type of f is unique; that is to say that f may never be referred to again by the caller of readLine2 after readLine2 returns, and this restriction is enforced by the type system. And since readLine2 does not return f itself but rather a new, different file object differentF, this means that it's impossible for readLine2 to be called with f as an argument ever again, thus preserving referential transparency while allowing for side effects to occur.
function `readLine2`(unique File f) returns (unique File, String)
return (differentF, line) where
String line = doImperativeReadLineSystemCall(f)
File differentF = newFileFromExistingFile(f)
end
end
function readLine(File f) returns String
return line where
String line = doImperativeReadLineSystemCall(f)
end
end
Now doImperativeReadLineSystemCall reads the next line from the file using an OS level system call which has the side effect of changing the current position in the file. But this violates referential transparency because calling it multiple times with the same argument will return different results each time as the current position in the file gets moved. This in turn makes readLine violate referential transparency because it calls doImperativeReadLineSystemCall. However, using uniqueness typing, we can construct a new version of readLine that is referentially transparent even though it's built on top of a function that's not referentially transparent:
function readLine2(unique File f) returns (unique File, String)
return (differentF, line) where
String line = doImperativeReadLineSystemCall(f)
File differentF = newFileFromExistingFile(f)
end
end
The unique declaration specifies that the type of f is unique; that is to say that f may never be referred to again by the caller of readLine2 after readLine2 returns, and this restriction is enforced by the type system. And since readLine2 does not return f itself but rather a new, different file object differentF, this means that it's impossible for readLine2 to be called with f as an argument ever again, thus preserving referential transparency while allowing for side effects to occur.
Ключевое слово `unique` указывает, что тип `f` является уникальным; то есть, вызывающий код `readLine2` не сможет обратиться к `f` повторно после возврата `readLine2`, и это ограничение обеспечивается системой типов. И поскольку `readLine2` не возвращает `f` напрямую, а возвращает новый, отличный файловый объект `differentF`, это означает, что `readLine2` невозможно будет вызвать с аргументом `f` снова, тем самым сохраняя референтную прозрачность и позволяя побочным эффектам происходить.
function readLine(File f) returns String
return line where
String line = doImperativeReadLineSystemCall(f)
end
end
Now doImperativeReadLineSystemCall reads the next line from the file using an OS level system call which has the side effect of changing the current position in the file. But this violates referential transparency because calling it multiple times with the same argument will return different results each time as the current position in the file gets moved. This in turn makes readLine violate referential transparency because it calls doImperativeReadLineSystemCall. However, using uniqueness typing, we can construct a new version of readLine that is referentially transparent even though it's built on top of a function that's not referentially transparent:
function readLine2(unique File f) returns (unique File, String)
return (differentF, line) where
String line = doImperativeReadLineSystemCall(f)
File differentF = newFileFromExistingFile(f)
end
end
The unique declaration specifies that the type of f is unique; that is to say that f may never be referred to again by the caller of readLine2 after readLine2 returns, and this restriction is enforced by the type system. And since readLine2 does not return f itself but rather a new, different file object differentF, this means that it's impossible for readLine2 to be called with f as an argument ever again, thus preserving referential transparency while allowing for side effects to occur.
Языки программирования
Типы уникальности реализованы в функциональных языках программирования, таких как Clean, Mercury, SAC и Idris. Иногда они используются для выполнения операций ввода-вывода в функциональных языках вместо монад. Для языка программирования Scala разработано расширение компилятора, использующее аннотации для обработки уникальности в контексте обмена сообщениями между акторами.
Отношение к линейной типизации
Уникальный тип очень похож на линейный тип, настолько, что эти термины часто используются как синонимы, но между ними есть существенная разница: фактическая линейная типизация позволяет приводить нелинейное значение к линейному типу, при этом сохраняя несколько ссылок на него. Уникальность гарантирует отсутствие других ссылок на значение, а линейность – невозможность создания новых ссылок на него. Различие между линейностью и уникальностью особенно заметно в контексте нелинейности и не уникальности, однако их можно объединить в единую систему типов.