Что такое экзистенциальный тип?


172

Я прочитал в Википедии статью Экзистенциальные типы . Я понял, что они называются экзистенциальными типами из-за экзистенциального оператора (∃). Я не уверен, какой в ​​этом смысл. какая разница между

T = ∃X { X a; int f(X); }

и

T = ∀x { X a; int f(X); }

?


8
Это может быть хорошей темой для programmers.stackexchange.com. programmers.stackexchange.com
jpierson

Ответы:


193

Когда кто-то определяет универсальный тип, ∀Xон говорит: вы можете подключить любой тип, какой хотите, мне не нужно ничего знать о типе, чтобы выполнять свою работу, я буду обозначать его только как непрозрачныйX .

Когда кто-то определяет экзистенциальный тип, ∃Xон говорит: я буду использовать любой тип, который мне нужен; Вы ничего не будете знать о типе, поэтому можете ссылаться на него только как непрозрачныйX .

Универсальные типы позволяют писать такие вещи, как:

void copy<T>(List<T> source, List<T> dest) {
   ...
}

copyФункция не имеет ни малейшего представления , что на Tсамом деле будет, но это не нужно.

Экзистенциальные типы позволят вам писать такие вещи, как:

interface VirtualMachine<B> {
   B compile(String source);
   void run(B bytecode);
}

// Now, if you had a list of VMs you wanted to run on the same input:
void runAllCompilers(List<∃B:VirtualMachine<B>> vms, String source) {
   for (∃B:VirtualMachine<B> vm : vms) {
      B bytecode = vm.compile(source);
      vm.run(bytecode);
   }
}

Каждая реализация виртуальной машины в списке может иметь свой тип байт-кода. runAllCompilersФункция не имеет ни малейшего представления о том , что тип байт - код, но это не нужно; все, что он делает, это передает байт-код из VirtualMachine.compileв VirtualMachine.run.

Подстановочные знаки типов Java (ex:) List<?>являются очень ограниченной формой экзистенциальных типов.

Обновление: забыл упомянуть, что вы можете сортировать экзистенциальные типы с помощью универсальных типов. Сначала оберните ваш универсальный тип, чтобы скрыть параметр типа. Во-вторых, инвертированный контроль (это эффективно меняет часть «ты» и «я» в вышеприведенных определениях, что является основным различием между экзистенциалами и универсалиями).

// A wrapper that hides the type parameter 'B'
interface VMWrapper {
   void unwrap(VMHandler handler);
}

// A callback (control inversion)
interface VMHandler {
   <B> void handle(VirtualMachine<B> vm);
}

Теперь мы можем иметь собственный VMWrapperвызов, VMHandlerкоторый имеет универсально типизированныйhandle функцией . Чистый эффект тот же, наш код должен восприниматься Bкак непрозрачный.

void runWithAll(List<VMWrapper> vms, final String input)
{
   for (VMWrapper vm : vms) {
      vm.unwrap(new VMHandler() {
         public <B> void handle(VirtualMachine<B> vm) {
            B bytecode = vm.compile(input);
            vm.run(bytecode);
         }
      });
   }
}

Пример реализации ВМ:

class MyVM implements VirtualMachine<byte[]>, VMWrapper {
   public byte[] compile(String input) {
      return null; // TODO: somehow compile the input
   }
   public void run(byte[] bytecode) {
      // TODO: Somehow evaluate 'bytecode'
   }
   public void unwrap(VMHandler handler) {
      handler.handle(this);
   }
}

12
@Kannan, +1 за очень полезный, но несколько трудный для понимания ответ: 1. Я думаю, что было бы полезно, если бы вы могли быть более откровенными о двойственной природе экзистенциальных и универсальных типов. Я только случайно понял, как вы сформулировали первые два абзаца очень похожим образом; только позже вы прямо заявите, что оба определения в основном одинаковы, но со словами «я» и «вы» поменялись местами. Кроме того, я не сразу понял, что подразумевается под «я» и «ты».
stakx - больше не вносит вклад

2
(продолжение :) 2. Я не совсем понимаю значение математической записи в List<∃B:VirtualMachine<B>> vmsили for (∃B:VirtualMachine<B> vm : vms). (Поскольку это универсальные типы, разве вы не могли использовать ?подстановочные знаки Java вместо «самодельного» синтаксиса?) Я думаю, что это может помочь иметь пример кода, в котором нет универсальных типов, таких как ∃B:VirtualMachine<B>задействованные, а вместо этого «прямой» ∃Bпотому что универсальные типы легко ассоциируются с универсальными типами после ваших первых примеров кода.
stakx - больше не вносит вклад

2
Раньше я ∃Bбыл явно о том, где происходит количественное определение. С подстановочным синтаксисом подразумевается квантификатор ( List<List<?>>фактически означает, ∃T:List<List<T>>а не List<∃T:List<T>>). Кроме того, явное количественное определение позволяет вам ссылаться на тип (я изменил пример, чтобы воспользоваться этим, сохранив байт-код типа Bво временной переменной).
Каннан Гундан

2
Используемая здесь математическая запись столь же небрежна, как ад, но я не думаю, что это вина ответчика (это стандарт). Тем не менее, лучше не злоупотреблять и экзистенциальные кванторы всеобщности таким образом , может быть ...
нолдорин

2
@Kannan_Goundan, я хотел бы знать, что заставляет вас говорить, что подстановочные знаки Java являются очень ограниченной версией этого. Знаете ли вы, что вы могли бы реализовать свою первую примерную функцию runAllCompilers на чистой Java (с помощью вспомогательной функции для получения (присвоения имени) параметра wilcard)?
LP_

107

Значение экзистенциального типа, такого ∃x. F(x) как пара, содержит некоторый тип x и значение типа F(x). Принимая во внимание, что значение полиморфного типа, подобного, ∀x. F(x)является функцией, которая принимает некоторый тип xи производит значение типа F(x). В обоих случаях тип закрывается над каким-то конструктором типа F.

Обратите внимание, что в этом представлении смешиваются типы и значения. Существующее доказательство - один тип и одно значение. Универсальное доказательство - это целое семейство значений, проиндексированных по типу (или отображение типов в значения).

Таким образом, разница между двумя указанными вами типами заключается в следующем:

T = ∃X { X a; int f(X); }

Это означает: значение типа Tсодержит вызываемый тип X, значение a:Xи функцию f:X->int. Производитель значений типа Tможет выбрать любой тип, Xа потребитель ничего не может знать X. За исключением того, что есть один пример его вызова aи что это значение можно превратить в значение int, передав его f. Другими словами, значение типа Tзнает, как произвести как- intто. Ну, мы могли бы исключить промежуточный тип Xи просто сказать:

T = int

Универсально выраженный немного отличается.

T = ∀X { X a; int f(X); }

Это означает: значение типа Tможет быть присвоено любому типу X, и оно будет производить значение a:Xи функцию f:X->int независимо от того, что Xесть . Другими словами: потребитель значений типа Tможет выбрать любой тип для X. И производитель значений типа Tвообще ничего не может знать X, но он должен быть в состоянии произвести значение aдля любого выбора Xи быть в состоянии превратить такое значение в int.

Очевидно, что реализация этого типа невозможна, потому что нет программы, которая могла бы создать значение каждого мыслимого типа. Если только вы не допускаете нелепостей вроде nullили низов.

Поскольку экзистенциальный является парой, экзистенциальный аргумент может быть преобразован в универсальный с помощью карри .

(∃b. F(b)) -> Int

такой же как:

∀b. (F(b) -> Int)

Первый является экзистенциальным ранга 2 . Это приводит к следующему полезному свойству:

Каждый экзистенциально квантифицированный тип ранга n+1является универсально квантифицированным типом ранга n.

Существует стандартный алгоритм превращения экзистенциалов в универсалии, который называется сколемизация .


7
Может быть полезно (или нет) упомянуть сколемизацию en.wikipedia.org/wiki/Skolem_normal_form
Джефф Риди,

34

Я думаю, что имеет смысл объяснять экзистенциальные типы вместе с универсальными типами, поскольку эти два понятия дополняют друг друга, то есть одно является «противоположностью» другого.

Я не могу ответить на каждую деталь об экзистенциальных типах (например, дать точное определение, перечислить все возможные варианты использования, их связь с абстрактными типами данных и т. Д.), Потому что я просто недостаточно осведомлен для этого. Я продемонстрирую только (с использованием Java), что в этой статье на HaskellWiki говорится, что это основной эффект экзистенциальных типов:

Экзистенциальные типы могут использоваться для нескольких различных целей. Но то, что они делают, это «скрывают» переменную типа с правой стороны. Обычно любая переменная типа, появляющаяся справа, также должна появляться слева […]

Пример установки:

Следующий псевдокод не совсем корректный Java, хотя это было бы достаточно легко исправить. На самом деле, это именно то, что я собираюсь сделать в этом ответе!

class Tree<α>
{
    α       value;
    Tree<α> left;
    Tree<α> right;
}

int height(Tree<α> t)
{
    return (t != null)  ?  1 + max( height(t.left), height(t.right) )
                        :  0;
}

Позвольте мне кратко изложить это для вас. Мы определяем ...

  • рекурсивный тип, Tree<α>представляющий узел в двоичном дереве. Каждый узел хранит valueнекоторый тип α и имеет ссылки на необязательныеleft и rightподдеревья того же типа.

  • функция height которая возвращает самое дальнее расстояние от любого конечного узла до корневого узла t.

Теперь давайте превратим приведенный выше псевдокод heightв правильный синтаксис Java! (Я буду продолжать опускать некоторые шаблоны для краткости, такие как модификаторы объектной ориентации и доступности.) Я собираюсь показать два возможных решения.

1. Универсальное типовое решение:

Наиболее очевидное исправление состоит в том, чтобы просто сделать heightgeneric, введя параметр типа α в его сигнатуру:

<α> int height(Tree<α> t)
{
    return (t != null)  ?  1 + max( height(t.left), height(t.right) )
                        :  0;
}

Это позволит вам объявлять переменные и создавать выражения типа α внутри этой функции, если вы захотите. Но...

2. Экзистенциальное решение типа:

Если вы посмотрите на тело нашего метода, вы заметите, что мы на самом деле не обращаемся и не работаем с чем-либо типа α ! Нет ни одного выражения с таким типом, ни переменных, объявленных с этим типом ... итак, почему мы вообще должны делать heightgeneric? Почему мы не можем просто забыть об α ? Как оказалось, мы можем:

int height(Tree<?> t)
{
    return (t != null)  ?  1 + max( height(t.left), height(t.right) )
                        :  0;
}

Как я уже писал в самом начале этого ответа, экзистенциальные и универсальные типы имеют комплементарный / двойственный характер. Таким образом, если решение универсального типа должно было сделать height более универсальным, то следует ожидать, что экзистенциальные типы будут иметь противоположный эффект: сделать его менее универсальным, а именно скрыть / удалить параметр типа α .

Как следствие, вы больше не можете ссылаться на тип t.valueв этом методе и манипулировать какими-либо выражениями этого типа, потому что с ним не связан ни один идентификатор. ( ?Подстановочный знак - это специальный токен, а не идентификатор, который «захватывает» тип.) t.valueФактически стал непрозрачным; пожалуй, единственное, что вы все еще можете сделать с ним, это набрать егоObject .

Резюме:

===========================================================
                     |    universally       existentially
                     |  quantified type    quantified type
---------------------+-------------------------------------
 calling method      |                  
 needs to know       |        yes                no
 the type argument   |                 
---------------------+-------------------------------------
 called method       |                  
 can use / refer to  |        yes                no  
 the type argument   |                  
=====================+=====================================

3
Хорошее объяснение. Вам не нужно приводить t.value к Object, вы можете просто ссылаться на него как Object. Я бы сказал, что экзистенциальный тип делает метод более универсальным из-за этого. Единственное, что вы можете когда-либо знать о t.value, это то, что это объект, тогда как вы могли бы сказать что-то конкретное о α (например, α расширяет Serializable).
Крейг П. Мотлин

1
Тем временем я пришел к выводу, что мой ответ на самом деле не объясняет, что такое экзистенции, и я подумываю написать еще один, более похожий на первые два абзаца ответа Каннана Гудана, который, я думаю, ближе к «истине». Тем не менее, @Craig: Сравнение обобщений с Objectдовольно интересным: хотя оба они похожи в том, что они позволяют писать статически независимый от типа код, первое (обобщение) не просто выбрасывает почти всю доступную информацию о типе в достичь этой цели. В этом конкретном смысле дженерики являются средством защиты ObjectИМО.
stakx - больше не вносит свой вклад

1
@stakx, в этом коде (от Effective Java) public static void swap(List<?> list, int i, int j) { swapHelper(list, i, j); } private static <E> void swapHelper(List<E> list, int i, int j) { list.set(i, list.set(j, list.get(i))); } , Eэто universal typeи ?представляет собой existential type?
Кевин Мередит

Этот ответ не верен. Тип ?in по- int height(Tree<?> t)прежнему неизвестен внутри функции и все еще определяется вызывающей стороной, поскольку именно вызывающая сторона должна выбирать, какое дерево передавать. Даже если люди называют это экзистенциальным типом в Java, это не так. ?Заполнитель может быть использован для реализации формы экзистенциалов в Java, в некоторых случаях, но это не один из них.
Питер Холл

15

Это все хорошие примеры, но я решил ответить на это немного по-другому. Напомним из математики, что ∀x. P (x) означает «для всех x, я могу доказать, что P (x)». Другими словами, это своего рода функция, вы даете мне крестик, и у меня есть метод, чтобы доказать это для вас.

В теории типов мы говорим не о доказательствах, а о типах. Таким образом, в этом пространстве мы имеем в виду «для любого типа X, который вы мне дадите, я дам вам конкретный тип P». Теперь, поскольку мы не даем P много информации о X, кроме того факта, что это тип, P не может ничего с этим поделать, но есть несколько примеров. P может создать тип «всех пар одного и того же типа»: P<X> = Pair<X, X> = (X, X). Или мы можем создать опцию type:, P<X> = Option<X> = X | Nilгде Nil - это тип нулевых указателей. Мы можем составить список из него: List<X> = (X, List<X>) | Nil. Обратите внимание, что последний является рекурсивным, значения List<X>либо являются парами, где первый элемент является X, а второй элемент являетсяList<X> либо это нулевой указатель.

Теперь в математике ∃x. P (x) означает «Я могу доказать, что существует конкретный x такой, что P (x) верен». Таких иксов может быть много, но для доказательства достаточно одного. Другой способ думать об этом состоит в том, что должен существовать непустой набор пар доказательство-доказательство {(x, P (x))}.

Переведено в теорию типов: тип в семействе ∃X.P<X>- это тип X и соответствующий тип P<X>. Обратите внимание на то, что до того, как мы передали X Р, (чтобы мы знали все о Х, но Р очень мало), теперь все наоборот.P<X>не обещает давать какую-либо информацию о X, только то, что там есть один, и что это действительно тип.

Чем это полезно? Ну, P может быть типом, который имеет способ раскрытия своего внутреннего типа X. Примером может быть объект, который скрывает внутреннее представление своего состояния X. Хотя у нас нет никакого способа непосредственно манипулировать им, мы можем наблюдать его эффект, poking at P. Может быть много реализаций этого типа, но вы можете использовать все эти типы независимо от того, какой из них был выбран.


2
Хм, но что получает функция, зная, что она P<X>вместо P(та же функциональность и тип контейнера, скажем, но вы не знаете, что она содержит X)?
Клаудиу

3
Строго говоря, ∀x. P(x)ничего не значит о доказуемости P(x), только правда.
R .. GitHub ОСТАНОВИТЬ ЛЬДА

11

Чтобы прямо ответить на ваш вопрос:

В универсальном типе использование Tдолжно включать параметр типа X. Например T<String>или T<Integer>. Для использования экзистенциального типа Tне включайте этот параметр типа, потому что он неизвестен или не имеет значения - просто используйте T(или в JavaT<?> ).

Дальнейшая информация:

Универсальные / абстрактные типы и экзистенциальные типы - это двойственная перспектива между потребителем / клиентом объекта / функции и производителем / реализацией этого. Когда одна сторона видит универсальный тип, другая видит экзистенциальный тип.

В Java вы можете определить универсальный класс:

public class MyClass<T> {
   // T is existential in here
   T whatever; 
   public MyClass(T w) { this.whatever = w; }

   public static MyClass<?> secretMessage() { return new MyClass("bazzlebleeb"); }
}

// T is universal from out here
MyClass<String> mc1 = new MyClass("foo");
MyClass<Integer> mc2 = new MyClass(123);
MyClass<?> mc3 = MyClass.secretMessage();
  • С точки зрения клиента из MyClass, Tявляется универсальным , так как вы можете заменить любой тип дляT когда вы используете этот класс , и вы должны знать фактический тип T всякий раз , когда вы используете экземплярMyClass
  • С точки зрения методов экземпляра MyClassсам по себе,T является экзистенциальным, потому что он не знает реальный типT
  • В Java ?представляет экзистенциальный тип - таким образом, когда вы находитесь внутри класса, Tв основном ?. Если вы хотите обработать экземпляр MyClassс помощью Texistential, вы можете объявить, MyClass<?>как в secretMessage()примере выше.

Экзистенциальные типы иногда используются, чтобы скрыть детали реализации чего-либо, как обсуждалось в другом месте. Java-версия этого может выглядеть так:

public class ToDraw<T> {
    T obj;
    Function<Pair<T,Graphics>, Void> draw;
    ToDraw(T obj, Function<Pair<T,Graphics>, Void>
    static void draw(ToDraw<?> d, Graphics g) { d.draw.apply(new Pair(d.obj, g)); }
}

// Now you can put these in a list and draw them like so:
List<ToDraw<?>> drawList = ... ;
for(td in drawList) ToDraw.draw(td);

Немного сложно понять это правильно, потому что я притворяюсь, что я использую какой-то функциональный язык программирования, а не Java. Но дело здесь в том, что вы фиксируете какое-то состояние и список функций, которые работают с этим состоянием, и вы не знаете реальный тип части состояния, но функции знают, так как они уже сопоставлены с этим типом. ,

Теперь в Java все не финальные не примитивные типы являются частично экзистенциальными. Это может показаться странным, но поскольку переменная, объявленная как, Objectпотенциально может быть подклассом Object, вы не можете объявить определенный тип, только «этот тип или подкласс». Итак, объекты представлены как бит состояния плюс список функций, которые работают с этим состоянием - какая именно функция вызывается, определяется во время выполнения поиском. Это очень похоже на использование экзистенциальных типов выше, где у вас есть часть экзистенциального состояния и функция, которая работает с этим состоянием.

В статически типизированных языках программирования без подтипов и приведений экзистенциальные типы позволяют управлять списками объектов различного типа. Список T<Int>не может содержать T<Long>. Однако список T<?>может содержать любой вариант T, позволяющий помещать в список множество различных типов данных и преобразовывать их все в целое число (или выполнять любые операции, предусмотренные в структуре данных) по требованию.

Можно в значительной степени всегда конвертировать запись с экзистенциальным типом в запись без использования замыканий. Закрытие также типизировано экзистенциально, так как свободные переменные, по которым оно закрыто, скрыты от вызывающей стороны. Таким образом, язык, который поддерживает замыкания, но не экзистенциальные типы, позволяет вам создавать замыкания, которые имеют то же скрытое состояние, которое вы бы поместили в экзистенциальную часть объекта.


11

Экзистенциальный тип - это непрозрачный тип.

Подумайте о дескрипторе файла в Unix. Вы знаете, что его тип int, поэтому вы можете легко его подделать. Например, вы можете попробовать прочитать из дескриптора 43. Если так получилось, что у программы есть файл, открытый с помощью этого конкретного дескриптора, вы будете читать из него. Ваш код не должен быть вредоносным, просто небрежным (например, дескриптор может быть неинициализированной переменной).

Экзистенциальный тип скрыт от вашей программы. Если fopenвозвращен экзистенциальный тип, все, что вы можете с ним сделать - это использовать его с некоторыми библиотечными функциями, которые принимают этот экзистенциальный тип. Например, следующий псевдокод будет компилироваться:

let exfile = fopen("foo.txt"); // No type for exfile!
read(exfile, buf, size);

Интерфейс "read" объявлен как:

Существует тип T такой, что:

size_t read(T exfile, char* buf, size_t size);

Переменная exfile - это не int, не a char*, не struct file - ничего, что вы можете выразить в системе типов. Вы не можете объявить переменную, тип которой неизвестен, и вы не можете привести, скажем, указатель к этому неизвестному типу. Язык не позволит вам.


9
Это не сработает. Если сигнатура readis, ∃T.read(T file, ...)тогда нет ничего, что вы могли бы передать в качестве первого параметра. Что бы сработало - fopenвернуть дескриптор файла и функцию чтения в одной и той же экзистенциальной∃T.(T, read(T file, ...))
форме

2
Кажется, это просто говорит о ADT.
kizzx2

7

Кажется, я немного опаздываю, но в любом случае, этот документ добавляет другое представление о том, что такое экзистенциальные типы, хотя они не являются специфически независимыми от языка, тогда должно быть довольно легко понять экзистенциальные типы: http: //www.cs.uu .nl / groups / ST / Projects / ehc / ehc-book.pdf (глава 8)

Различие между универсально и экзистенциально определенным типом может быть охарактеризовано следующим наблюдением:

  • Использование значения с квантифицированным типом определяет тип, который нужно выбрать для создания экземпляра квантифицированной переменной типа. Например, вызывающая функция идентификатора «id :: aaa → a» определяет тип, который нужно выбрать для переменной типа a для этого конкретного применения id. Для приложения функции «id 3» этот тип равен Int.

  • Создание значения с квантифицированным типом определяет и скрывает тип переменной квантифицированного типа. Например, создатель «.a. (A, a → Int)» мог построить значение этого типа из «(3, λx → x)»; другой создатель построил значение с таким же типом из «(« x », λx → ord x)». С точки зрения пользователя оба значения имеют одинаковый тип и, таким образом, являются взаимозаменяемыми. У значения есть определенный тип, выбранный для переменной типа a, но мы не знаем, какой тип, поэтому эта информация больше не может быть использована. Это значение конкретного типа информации было «забыто»; мы только знаем, что это существует.


1
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится.
Шейлак

1
@sheilak: обновил ответ, спасибо за предложение
themarketka

5

Универсальный тип существует для всех значений параметра (ов) типа. Экзистенциальный тип существует только для значений параметров типа (ов), которые удовлетворяют ограничениям экзистенциального типа.

Например, в Scala одним из способов выражения экзистенциального типа является абстрактный тип, который ограничен некоторыми верхними или нижними границами.

trait Existential {
  type Parameter <: Interface
}

Эквивалентно ограниченный универсальный тип - это экзистенциальный тип, как в следующем примере.

trait Existential[Parameter <: Interface]

Любое использование сайта может использовать, Interfaceпотому что любые инстанцируемые подтипы Existentialдолжны определять, type Parameterкоторый должен реализовывать Interface.

Вырожденный случай экзистенциального типа в Scala является абстрактным типом , который никогда не упоминается и , следовательно , не должно быть определен любым подтипом. Это эффективно имеет сокращенную запись List[_] в Scala и List<?>в Java.

Мой ответ был вдохновлен предложением Мартина Одерского объединить абстрактные и экзистенциальные типы. Сопровождающий слайд помогает пониманию.


1
Прочитав часть материала выше, кажется, вы хорошо подытожили мое понимание: универсальные типы ∀x.f(x)непрозрачны для своих принимающих функций, в то время как экзистенциальные типы ∃x.f(x)ограничены определенными свойствами. Как правило, все параметры являются экзистенциальными, так как их функция будет манипулировать ими напрямую; однако универсальные параметры могут иметь типы, которые являются универсальными, так как функция не будет управлять ими, кроме базовых универсальных операций, таких как получение ссылки, как в:∀x.∃array.copy(src:array[x] dst:array[x]){...}
Джордж

Как описано здесь, элементы типа stackoverflow.com/a/19413755/3195266 могут эмулировать универсальное количественное определение через тип идентификации. И наверняка есть forSomeдля экзистенциального количественного определения параметров типа.
нэцу

3

Исследования абстрактных типов данных и сокрытия информации привели экзистенциальные типы в языки программирования. Создание абстрактного типа данных скрывает информацию об этом типе, поэтому клиент этого типа не может злоупотреблять им. Скажем, у вас есть ссылка на объект ... некоторые языки позволяют вам преобразовать эту ссылку в ссылку на байты и делать все, что вы хотите, с этим фрагментом памяти. В целях гарантии поведения программы для языка полезно обеспечить, чтобы вы действовали только в отношении ссылки на объект с помощью методов, предоставляемых конструктором объекта. Вы знаете, что тип существует, но не более того.

Видеть:

Абстрактные типы имеют экзистенциальный тип, MITCHEL & PLOTKIN

http://theory.stanford.edu/~jcm/papers/mitch-plotkin-88.pdf


1

Я создал эту диаграмму. Я не знаю, строго ли это. Но если это поможет, я рад. введите описание изображения здесь


-6

Как я понимаю, это математический способ описания интерфейсов / абстрактного класса.

Что касается T = ∃X {X a; int f (X); }

Для C # это будет переводиться в общий абстрактный тип:

abstract class MyType<T>{
    private T a;

    public abstract int f(T x);
}

«Экзистенциальный» просто означает, что существует некоторый тип, который подчиняется определенным здесь правилам.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.