Design: Предложение: добавить межъязыковые привязки типов

Созданный на 1 мая 2019  ·  61Комментарии  ·  Источник: WebAssembly/design

WebAssembly в настоящее время очень хорошо выполняет код, написанный на произвольных языках, из заданного (обычно JS) интерпретатора, но ему не хватает нескольких ключевых функций, когда дело доходит до объединения нескольких произвольных языков вместе .

Одна из этих функций - система типов, не зависящая от языка. Я хотел бы предложить добавить одну или несколько таких систем в WebAssembly.

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

Зачем заботиться о языковой совместимости?

Преимущества более низкого языкового барьера включают:

  • Больше библиотек для пользователей wasm : это само собой разумеется, но улучшение языковой совместимости означает, что пользователи могут чаще использовать существующие библиотеки, даже если библиотека написана на другом языке, чем они используют.

  • Более легкое освоение малых языков: на нынешнем рынке часто бывает трудно добиться успеха языкам без корпоративной поддержки. Новые языки (и даже языки, подобные D, которые годами совершенствовались) должны конкурировать с языками с крупными экосистемами и страдать от недостатка собственных библиотек. Совместимость языков позволит им использовать существующие экосистемы, такие как Python или Java.

  • Улучшенные не зависящие от языка инструментальные цепочки : сейчас у большинства языков есть собственная схема загрузки библиотек и менеджер пакетов (или, в случае C / C ++, несколько неофициальных). Написать независимый от языка конструктор проекта сложно, потому что эти языки часто имеют тонкие зависимости и несовместимости ABI, для решения которых требуется монолитное решение для всего проекта. Надежная межъязыковая система типов упростит разделение проектов на более мелкие модули, которые можно будет обрабатывать с помощью решения, подобного npm.

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

Требования

Имея это в виду, я хочу обрисовать требования, которым должна соответствовать межъязыковая система типов.

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

Чтобы быть действительно полезной в настройке wasm, такая система типов потребует:

1 - Безопасность

  • Типобезопасность: вызываемый должен иметь доступ только к данным, указанным вызывающим, в стиле объектных возможностей.

    • Память надо «забыть» по окончании разговора. Вызываемый абонент не должен иметь возможность получить доступ к данным вызывающего абонента, выполнить возврат, а затем снова получить доступ к этим данным в любой форме.

2 - Накладные расходы

  • Разработчикам должно быть удобно регулярно совершать межмодульные вызовы, например, в цикле рендеринга.

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

3 - Структурные графики

  • Система типов должна включать структуры, необязательные указатели, массивы переменной длины, срезы и т. Д.

    • В идеале вызывающая сторона должна иметь возможность отправить граф объектов, разбросанных по памяти, при соблюдении требований 1 и 2.

4 - Типы ссылок

  • Модули должны иметь возможность обмениваться ссылочными типами, вложенными глубоко в структурные графы.

5 - Мост между раскладками памяти

  • Это очень важный момент. К разным категориям языков предъявляются разные требования. Языки, полагающиеся на линейную память, захотят передавать срезы памяти, тогда как языки, полагающиеся на GC, захотят передать ссылки на GC.

    • Идеальная система типов должна выражать семантические типы и позволять языкам решать, как их интерпретировать в памяти. Хотя передача данных между языками с несовместимыми макетами памяти всегда будет сопряжена с некоторыми накладными расходами, передача данных между похожими языками в идеале должна быть дешевой (например, устройства для внедрения должны избегать шагов сериализации-десериализации, если memcpy может выполнять ту же работу).

    • Дополнительные привязки также могут допускать кеширование и другие стратегии оптимизации.

    • Работа по преобразованию при передаче данных между двумя модулями должна быть прозрачной для разработчика, если семантические типы совместимы.

6 - Обработка ошибок во время компиляции

  • Любая ошибка, связанная с недопустимыми аргументами вызова функции, должна быть обнаруживаемой и выражаемой во время компиляции, в отличие, например, от JS, где TypeErrors генерируются во время выполнения при попытке оценить аргумент.
  • В идеале компиляторы языка сами должны обнаруживать ошибки типов при импорте модулей wasm и выводить пользователю выразительные идиоматические ошибки. Какую форму должна принимать эта проверка ошибок, необходимо будет подробно описать в репозитории соглашений об инструментах .
  • Это означает, что IDL с существующими конвертерами на другие языки будет плюсом.

7 - Обеспечьте точку Шеллинга для межъязыкового взаимодействия

  • Это легче сказать, чем сделать, но я думаю, что wasm должен послать сигнал всем разработчикам компиляторов, что стандартным способом взаимодействия между языками является X. По очевидным причинам наличие нескольких конкурирующих стандартов языковой совместимости нежелательно.

Предлагаемая реализация

Я предлагаю добавить привязки к @kentonv в Webassembly.

Они будут работать аналогично привязкам WebIDL: модули wasm будут экспортировать функции и использовать специальные инструкции для привязки их к типизированным сигнатурам; другие модули будут импортировать эти подписи и связывать их со своими функциями.

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

Все инструкции привязки Capn'proto будут храниться в новом разделе привязок Cap'n'proto .

Типы Cap'n'proto

Для стандарта потребуется внутреннее представление языка схем capnproto . Например, следующий тип Capnproto:

`` Капитан Прото
struct Person {
имя @ 0 : Текст;
дата рождения @ 3 : Дата;

электронная почта @ 1 : Текст;
телефоны @ 2 : Список (PhoneNumber);

struct PhoneNumber {
число @ 0 : текст;
type @ 1 : Тип;

enum Type {
  mobile @0;
  home @1;
  work @2;
}

}
}

struct Date {
год @ 0 : Int16;
месяц @ 1 : UInt8;
день @ 2 : UInt8;
}

might be represented as

```wasm
(<strong i="32">@capnproto</strong> type $Date (struct
    (field "year" Int16)
    (field "month" UInt8)
    (field "day" UInt8)
))
(<strong i="33">@capnproto</strong> type $Person_PhoneNumber_Type (enum 0 1 2))
(<strong i="34">@capnproto</strong> type $Person_PhoneNumber (struct
    (field "number" Text)
    (field "type" $Person_PhoneNumber_Type)
))
(<strong i="35">@capnproto</strong> type $Person (struct
    (field "name" Text)
    (field "email" Text)
    (field "phones" (generic List $Person_PhoneNumber))
    (field "birthdate" $Data)
))

Сериализация из линейной памяти

Сообщения Capnproto передают два типа данных: сегменты (необработанные байты) и возможности.

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

(Можно было бы разработать лучший подход к возможностям, чтобы избежать проверки типов во время выполнения.)

Обратите внимание, что фактические вычисления сериализации будут происходить в связующем коде, если вообще будут выполняться (см. Создание связующего кода ).

Операторы привязки

| Оператор | Немедленно | Дети | Описание |
| : --- | : --- | : --- | : --- |
| сегмент | off ‑ idx
len ‑ idx | | Принимает значения wasm off-idx 'th и len-idx ' th исходного кортежа, которые оба должны быть i32 s, в качестве смещения и длины фрагмента линейной памяти в какой сегмент хранится. |
| captable | off ‑ idx
len ‑ idx | | Принимает значения wasm off-idx 'th и len-idx ' th исходного кортежа, которые оба должны быть i32 s, в качестве смещения и длины фрагмента таблицы, в которой таблица возможностей сохраняется. |
| сообщение | capnproto-тип
таблица возможностей | сегменты | Создает сообщение capnproto в формате capnproto-type , используя предоставленную таблицу возможностей и сегменты. |

Сериализация из управляемой памяти

Трудно определить конкретное поведение до того, как предложение GC будет принято. Но общая реализация такова, что привязки capnproto будут использовать один оператор преобразования для получения типов capnproto из типов GC.

Правила преобразования для низкоуровневых типов будут довольно простыми: i8 преобразуется в Int8, UInt8 и bool, i16 преобразуется в Int16 и т. Д. Типы высокого уровня преобразуются в их эквиваленты capnproto: ссылки на структуру и массивы преобразуются в указатели, непрозрачные ссылки преобразовать в возможности.

Для более полного предложения потребуется определить стратегию для enum и объединений.

Операторы привязки

| Оператор | Немедленно | Дети | Описание |
| : --- | : --- | : --- | : --- |
| как | capnproto-тип
idx | | Принимает значение wasm idx 'th исходного кортежа, которое должно быть ссылкой, и создает значение capnproto capnproto-type . |

Десериализация в линейную память

Десериализация в линейную память в основном похожа на сериализацию из нее, с одной добавленной оговоркой: код wasm часто не знает заранее, сколько памяти займет тип capnproto, и должен предоставить хосту какой-то метод динамического управления памятью. .

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

Другое решение - разрешить входящим картам привязки связываться с двумя входящими выражениями привязки (и, следовательно, с двумя функциями): одно, которое выделяет память для данных capnproto, и другое, которое фактически принимает данные.

Десериализация в управляемую память

Десериализация в управляемую память будет использовать тот же оператор преобразования, что и противоположное направление.

Создание кода клея

При связывании двух модулей wasm вместе (статически или динамически) средство внедрения должно перечислить все типы capnproto, общие для обоих модулей, привязки между типами функций и типами capnproto, а также сгенерировать связующий код для каждой пары типов функций.

Код клея будет зависеть от типов связанных данных. Связующий код между привязками линейной памяти сводился бы к вызовам memcpy. Связующий код между привязками управляемой памяти сводился бы к передаче ссылок. С другой стороны, связующий код между линейной и управляемой памятью потребует более сложных вложенных операций преобразования.

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

Альтернативные решения

В этом разделе я рассмотрю альтернативные способы обмена данными и их оценку по метрикам, определенным в разделе « Требования ».

Обмен сообщениями JSON

Это решение методом грубой силы. Я не собираюсь тратить на это много времени, потому что его недостатки довольно очевидны. Он не соответствует требованиям 2, 4 и 6.

Отправлять необработанные байты, закодированные в формате сериализации

Это частичное решение. Определите способ для модулей wasm передавать срезы линейной памяти и таблиц в другие модули, и разработчики модулей могут затем использовать формат сериализации (capnproto, protobuff или какой-либо другой) для кодирования структурированного графа в последовательность байтов, передачи байтов, и используйте тот же формат для его декодирования.

Он передает 1 и 3, и он может передать 2 и 4 с некоторой настройкой (например, передать ссылки как индексы в таблицу). Он может пройти 6, если пользователь обязательно экспортирует тип сериализации в определение типа на языке вызывающего.

Однако это не соответствует требованиям 5 и 7. Это непрактично при связывании между двумя реализациями GC; например, модуль Python, вызывающий библиотеку Java с помощью Protobuf, должен будет сериализовать словарь как линейную память, передать этот фрагмент памяти и затем десериализовать его как объект Java, вместо этого сделав несколько поисков по хеш-таблице, которые можно оптимизировать в реализация JIT.

И это побуждает каждого автора библиотеки использовать свой собственный формат сериализации (JSON, Protobuf, FlatBuffer, Cap'n Proto, SBE), который не идеален для взаимодействия; хотя это можно было бы облегчить, определив канонический формат сериализации в соглашениях об инструментах .

Однако добавление возможности передавать произвольные срезы линейной памяти было бы хорошим первым шагом.

Отправить объекты GC

Можно было бы полагаться на модули, отправляющие друг другу объекты GC .

У решения есть некоторые преимущества: предложение GC уже реализуется; он передает 1, 3, 4 и 7. Данные, собранные сборщиком мусора, дорого выделять, но дешево передавать.

Однако это решение не идеально для C-подобных языков. Например, модуль D, передающий данные в модуль Rust, должен будет сериализовать свои данные в граф GC, передать граф функции Rust, которая десериализует его в свою линейную память. Этот процесс выделяет узлы GC, которые немедленно отбрасываются из-за большого количества ненужных накладных расходов.

Помимо этого, текущее предложение GC не имеет встроенной поддержки перечислений и объединений; и обработка ошибок будет либо во время компоновки, либо во время выполнения, а не во время компиляции, если компилятор не может читать и понимать типы wasm GC.

Используйте другие кодировки

Любая библиотека сериализации, которая определяет систему типов, может работать с wasm.

Capnproto кажется наиболее подходящим из-за его акцента на нулевом копировании и его встроенных объектных возможностей, которые аккуратно отображаются на ссылочные типы.

Оставшаяся работа

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

  • Операторы привязки
  • Эквивалентности типов GC
  • Возможности объекта
  • Массивы Bool
  • Массивы
  • Константы
  • Дженерики
  • Эволюция типов
  • Добавьте третий тип привязки "геттеры и сеттеры".
  • Возможные стратегии кеширования
  • Поддержка нескольких таблиц и линейной памяти

Между тем, любые отзывы о том, что я уже написал, будут приветствоваться. Возможности здесь довольно обширны, поэтому я был бы признателен за помощь в уточнении вопросов, на которые необходимо ответить в этом предложении.

Самый полезный комментарий

мы можем добавить несколько привязок для каждого типа IR, чтобы охватить подавляющее большинство языков.

Это ключевое предположение, которое, как я считаю, просто неверно. Мой опыт показывает, что существует (по крайней мере!) Столько же вариантов представления, сколько и языковых реализаций. И они могут быть сколь угодно сложными.

Возьмите V8, в котором только есть несколько десятков (!) Представлений для струн, включая различные кодировки, разнородные связки и т. Д.

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

Другие языки используют забавные представления для длины строки или не хранят ее явно, но требуют ее вычисления.

Эти два примера уже показывают, что декларативный макет данных не сокращает его, вам часто нужно иметь возможность вызывать код времени выполнения, который, в свою очередь, может иметь свои собственные соглашения о вызовах.

И это просто строки, которые концептуально представляют собой довольно простой тип данных. Я даже не хочу думать о бесконечном количестве способов, которыми языки представляют типы продуктов (кортежи / структуры / объекты).

И затем есть принимающая сторона, где вы должны иметь возможность создавать все эти структуры данных!

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

Все 61 Комментарий

Это действительно интересно! Я только быстро прочитал, и у меня есть только некоторые начальные мысли, но мой первый и главный вопрос - это спросить, почему существующий механизм FFI, который уже предоставляет / использует большинство языков, недостаточен для WebAssembly. Практически каждый язык, с которым я знаком, имеет некоторую форму C FFI и, таким образом, уже сегодня может взаимодействовать. Многие из этих языков также могут выполнять статическую проверку типов на основе этих привязок. Более того, для этих интерфейсов уже существует множество инструментов (например, ящик bindgen для Rust, erl_nif для Erlang / BEAM и т. Д.). C FFI уже отвечает самым важным требованиям и имеет ключевое преимущество в том, что он уже зарекомендовал себя и широко используется на практике.

5 - Мост между раскладками памяти

Идеальная система типов должна выражать семантические типы и позволять языкам решать, как их интерпретировать в памяти. Хотя передача данных между языками с несовместимыми макетами памяти всегда будет сопряжена с некоторыми накладными расходами, передача данных между похожими языками в идеале должна быть дешевой (например, устройства для внедрения должны избегать шагов сериализации-десериализации, если memcpy может выполнять ту же работу).

Работа по преобразованию при передаче данных между двумя модулями должна быть прозрачной для разработчика, если семантические типы совместимы.

Прозрачный перевод одного макета в другой при передаче данных через барьер FFI действительно кажется мне задачей для бэкэндов компилятора или языковых сред, и, вероятно, вообще нежелателен для языков, чувствительных к производительности, таких как C / C ++ / Rust / и т. Д. В частности, для вещей, которые вы планируете передавать через FFI, мне всегда было бы предпочтительнее использовать общий ABI, чем выполнять какой-либо перевод, поскольку перевод, вероятно, повлечет за собой слишком высокие затраты. Преимущество выбора макета, отличного от обычного ABI платформы, вряд ли того стоит, но я с готовностью признаю, что могу неправильно понять, что вы подразумеваете под альтернативными макетами.

Кроме того, возложение бремени надежных инструментов FFI на компиляторы / среды выполнения имеет дополнительное преимущество в том, что любые сделанные улучшения применимы на других платформах, и наоборот, поскольку улучшения FFI для платформ, отличных от Wasm, приносят пользу Wasm. Я думаю, что аргумент должен быть действительно убедительным, чтобы начать с нуля и построить новый механизм ИФИ.

Приносим извинения, если я неправильно понял цель предложения или пропустил что-то важное, как я упоминал выше, мне нужно прочитать еще раз более внимательно, но я чувствовал, что мне нужно задать свои первоначальные вопросы, пока у меня было немного времени.

Для этого тоже существует Apache Arrow , но он больше ориентирован на высокопроизводительные приложения.

Я думаю, что согласен с общей мотивацией здесь, и это в основном совпадает с обсуждениями, которые у нас были, о том, как можно обобщить привязки Web IDL в будущем. Действительно, более ранние проекты объяснителя содержали запись в часто задаваемых вопросах, в которой упоминался этот вариант межъязыкового использования.

Моя основная проблема (и причина для исключения этой записи в FAQ) - это область действия: общая проблема связывания N языков, вероятно, вызовет много открытых (и, возможно, нескончаемых) обсуждений, особенно с учетом того, что никто уже не делает этого ( что, конечно же, является проблемой курицы и яйца). Напротив, проблемы, решаемые Web IDL Bindings, довольно конкретны и легко демонстрируются с помощью Rust / C ++ сегодня, что позволяет нам мотивировать (нетривиальные) усилия по стандартизации / реализации, а также с готовностью прототипировать / проверять предлагаемое решение.

Но я надеюсь, что привязки Web IDL позволят нам решить эту проблему «курицы и яйца» и начать получать некоторый опыт межъязыкового связывания, который может послужить стимулом для следующей волны расширений или чего-то нового, не специфичного для Web IDL . (Обратите внимание, что, как в настоящее время предлагается, если два модуля wasm, использующие совместимые привязки Web IDL, вызывают друг друга, оптимизирующий impl может выполнить оптимизацию, о которой вы говорите здесь; только без полной выразительности Cap'n Proto.)

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

б. По определению, любой ЖК-дисплей систем типов не гарантирует захват всего языка типов данного языка. Это оставляет разработчикам языка крайне неудобный выбор: поддерживать свой собственный язык или отказываться от преимуществ системы типов своего языка. Показательный пример: в Haskell есть «классы типов». Любая реализация Haskell, которая включает в себя не поддерживающие классы типов, фактически уничтожит его и сделает непригодным для использования.
Другой пример: поддержка универсальных шаблонов в C ++ требует исключения универсальности во время компиляции; с другой стороны, ML, Java (и множество других языков) используют форму универсального представления, что несовместимо с подходом, принятым в C ++.

С другой стороны, наличие двух выражений экспортируемого / импортированного типа, кажется, поднимает свои собственные проблемы: должна ли языковая система проверять, что эти два выражения в каком-то смысле согласованы? Кто несет ответственность за выполнение этой работы?

@lukewagner Спасибо за ссылки! Определенно рад, что мне представилась возможность прочитать этот документ!

Мне кажется, что в этом конкретном обсуждении как бы смешаны две вещи - кое-что из того, что ниже, написано, чтобы я мог дважды проверить свое понимание, поэтому не стесняйтесь указывать на все, что я, возможно, неправильно понял или пропустил:

  1. Эффективные привязки хоста

    • В основном проблема WebIDL предназначена для решения, по крайней мере, для сред браузера - описание интерфейса, которое отображает модуль-> хост и хост-> модуль, по существу делегируя работу по преобразованию от одного к другому движку хоста. Этот перевод не обязательно будет идеальным или даже оптимизированным, но оптимизирующие движки могут использовать его для этого. Однако даже оптимизированный перевод все еще выполняется в некоторой степени, но это приемлемо, потому что альтернативой остается перевод, только медленнее.

  2. Эффективные гетерогенные привязки модулей к модулям.

    • Другими словами, учитывая два модуля, один из которых записан в source а другой в dest , типы разделяются между ними, вызывая из source-> dest и / или dest-> source

    • Если общий FFI недоступен и дан что-то вроде WebIDL, то есть с использованием 1, неоптимизированный путь будет заключаться в преобразовании через некоторый общий тип знаменателя, предоставляемый средой хоста при вызове через языковые барьеры, например source type -> common type -> dest type .



      • Оптимизирующий движок теоретически мог бы сделать этот перевод напрямую из source в dest без посредников, но все равно накладывает накладные расходы на перевод.



    • Если доступен общий FFI, то есть source и dest совместно используют ABI (например, C ABI), тогда source и dest могут вызывать друг друга напрямую с помощью никаких накладных расходов через FFI. Это, наверное, наиболее вероятный сценарий на практике.

Итак, я считаю, что использование WebIDL или чего-то подобного (то есть надмножества, которое поддерживает более широкий набор API-интерфейсов хоста / сред) определенно дает преимущества, но на самом деле это всего лишь решение проблемы, описанной в 1, и подмножество из 2, который касается межъязыковых привязок, когда FFI недоступен. Подмножество из 2, где FFI _is_ доступно, явно предпочтительнее альтернатив, поскольку оно само по себе не влечет за собой накладных расходов.

Есть ли веские причины для использования IDL, даже если FFI является вариантом? Чтобы быть ясным, я определенно согласен с использованием IDL для других упомянутых случаев использования, но я конкретно спрашиваю в контексте языковой совместимости, а не привязки хоста.

У меня есть пара дополнительных вопросов, если и C FFI (например, поскольку он наиболее распространен), и IDL используются / присутствуют одновременно:

  • Если оба языка source и dest предоставляют разные определения типов для общего типа с одним и тем же базовым представлением в памяти в соответствии с их общим ABI (например, общее представление для массива переменной длины ) - будет ли движок хоста пытаться выполнить преобразование между этими типами только потому, что присутствуют директивы IDL, даже если они могут безопасно вызывать друг друга, используя свой стандартный FFI?

    • Если нет, и это вариант выбора, это кажется идеальным сценарием, поскольку вы можете добавить IDL для поддержки взаимодействия с языками без FFI, одновременно поддерживая языки с FFI. Я не уверен, как хост-движок может заставить это работать. Я не обдумал это полностью, так что я, наверное, что-то упускаю

    • Если да, то как хост-движок объединяет типы ?:



      • Если движок заботится только о макете, то как статический анализ может определить, когда вызывающий объект предоставляет вызываемому некорректные типы аргументов? Если такой анализ не является целью, то может показаться, что IDL действительно идеально подходит только для связывания хостов, и в меньшей степени для межъязыкового взаимодействия.


      • Если движку важна не только компоновка, то, другими словами, система типов требует как номинальной, так и конструктивной совместимости:





        • Кто определяет авторитетный тип для какой-либо функции? Как мне вообще сослаться на авторитетный тип на каком-то языке? Например, предположим, что я вызываю общую библиотеку, написанную на другом языке, которая определяет функцию add/2 , а add/2 ожидает два аргумента некоторого типа size_t . Мой язык не обязательно знает о size_t номинально, он имеет собственное ABI-совместимое представление беззнаковых целых чисел машинной ширины, usize , поэтому привязки FFI для этой функции на моем языке используют мои типы языков. Учитывая это, как мой компилятор может узнать, что нужно генерировать IDL, который отображает usize в size_t .






  • Существуют ли примеры интерфейсов IDL, используемых для вызова между модулями в программе, где FFI доступен, но явно не используется в пользу интерфейса, описанного в IDL? Конкретно что-то не WebAssembly, в основном интересно изучить преимущества в тех случаях.

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

@bitwalker

Это действительно интересно!

Рад что вам понравилось!

но мой первый и главный вопрос - это спросить, почему существующий механизм FFI, который уже предоставляет / использует большинство языков, недостаточен для WebAssembly.

У системы типа C есть несколько проблем в качестве межъязыкового IDL:

  • Он работает на основе общего адресного пространства, что небезопасно и намеренно не поддерживается в WebAssembly. (мой собственный опыт работы с FFI с JS-to-C показывает, что реализации, как правило, торгуют безопасностью в обмен на скорость)

  • В нем нет встроенной поддержки массивов динамической длины, помеченных объединений, значений по умолчанию, обобщений и т. Д.

  • Нет прямого эквивалента ссылочным типам.

C ++ решает некоторые из этих проблем (не самая большая проблема - общее адресное пространство), но добавляет ряд концепций, которые на самом деле бесполезны в IPC. Конечно, вы всегда можете использовать надмножество C или подмножество C ++ в качестве IDL, а затем разработать для него правила привязки, но в этот момент вы почти не получаете преимуществ от существующего кода, поэтому вы также можете использовать существующий IDL.

В частности, для вещей, которые вы планируете передавать через FFI.

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

Преимущество выбора макета, отличного от обычного ABI платформы, вряд ли того стоит, но я с готовностью признаю, что могу неправильно понять, что вы подразумеваете под альтернативными макетами.

Дело в том, что сейчас общий ABI - это кусок байтов, хранящийся в линейной памяти. Но в будущем, когда будет реализовано предложение GC, некоторые языки (Java, C #, Python) будут хранить очень мало или ничего в линейной памяти. Вместо этого они будут хранить все свои данные в структурах GC. Если два из этих языков попытаются установить связь, сериализация этих структур в поток байтов только для их немедленной десериализации будет ненужными накладными расходами.


@KronicDeth Спасибо, я разберусь.

Хотя, просматривая документ, кажется, что это надмножество Flatbuffers, специально предназначенное для повышения производительности? В любом случае, каковы его качества, которые могут однозначно помочь совместимости модуля WebAssembly по сравнению с Flatbuffers или Capnproto?


@lukewagner

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

Согласовано. При написании этого предложения я предполагал, что любая реализация привязок capnproto будет основана на обратной связи от реализации предложения WebIDL.

Моя основная проблема (и причина для исключения этой записи в FAQ) - это область действия: общая проблема связывания N языков, вероятно, вызовет много открытых (и, возможно, нескончаемых) обсуждений, особенно с учетом того, что никто уже не делает этого ( что, конечно же, является проблемой курицы и яйца).

Я думаю, что обсуждение реализации capnproto имеет смысл, даже если это так рано.

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

Что касается проблемы N-to-N, я сосредотачиваюсь на следующих решениях:

  • Беспокойтесь только о передаче данных в стиле RPC. Не пытайтесь передавать совместно используемые изменяемые данные, классы, время жизни указателя или любой другой тип информации, более сложной, чем «вектор имеет три поля: 'x', 'y' и 'z', которые все являются плавающими».

  • Попробуйте сгруппировать языки и варианты использования в «кластеры» стратегий обработки данных. Разработать стратегии в центре этих кластеров; компиляторы языка связываются с заданной стратегией, а интерпретатор выполняет остальную работу NxN.


@fgmccabe

Причина в том, что я считаю задачу невыполнимой. Для этого есть две основные причины:
а. У разных языков разная семантика, которая не обязательно отражается в аннотации типа. В данном случае оценка Prolog радикально отличается от оценки C ++: до такой степени, что языки по существу не совместимы. (Для Prolog вы можете заменить несколько других языков)

Любая реализация Haskell, которая включает в себя не поддерживающие классы типов, фактически уничтожит его и сделает непригодным для использования.

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

Тем не менее, я думаю, что у большинства языков есть некоторые сходства в том, как они структурируют свои данные (например, у них есть способ сказать «у каждого человека есть имя, адрес электронной почты и возраст» или «у каждой группы есть список людей. произвольного размера »).

Я думаю, что можно использовать это сходство, чтобы значительно уменьшить трение между модулями. (см. также мой ответ Люкевагнеру)

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

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

С другой стороны, наличие двух выражений экспортируемого / импортированного типа, кажется, поднимает свои собственные проблемы: должна ли языковая система проверять, что эти два выражения в каком-то смысле согласованы? Кто несет ответственность за выполнение этой работы?

Да, я изначально хотел включить раздел о проверке инвариантов и еще один о совместимости типов, но потерял смелость.

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

Система типа C имеет несколько проблем в качестве межъязыкового IDL.

Чтобы быть ясным, я не предлагаю это как IDL. Скорее я предполагаю, что двоичный интерфейс (C ABI) уже существует, четко определен и уже имеет обширную языковую поддержку. Отсюда следует, что WebAssembly не нужно предоставлять другое решение, если решаемая проблема не выходит за рамки межъязыкового взаимодействия.

Он работает на основе общего адресного пространства, что небезопасно и намеренно не поддерживается в WebAssembly.

Так что я думаю, что вижу здесь часть недоразумения. Мы говорим о двух классах FFI, один из которых предполагает совместное использование линейной памяти (более традиционный FFI с общей памятью), а другой - нет (более традиционный IPC / RPC). Я говорил о первом, и я думаю, что вы больше сосредоточены на втором.

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

Межмодульное взаимодействие, которое _не_ использует FFI с общей памятью, то есть IPC / RPC, определенно кажется хорошим совпадением для WebIDL, capnproto или одного из других предложений в этом ключе, поскольку это их хлеб с маслом.

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

В нем нет встроенной поддержки массивов динамической длины, помеченных объединений, значений по умолчанию, обобщений и т. Д.

Я думаю, что это, вероятно, не актуально сейчас, когда я понимаю, что мы говорили о двух разных вещах, но только для потомков: ABI, безусловно, имеет _представление_ для массивов переменной длины и помеченных объединений, но вы правы в том, что C действительно имеет слабая система типов, но дело не в этом, языки не нацелены на C FFI для системы типов C. Причина, по которой C ABI полезен, заключается в том, что он предоставляет общий знаменатель, который языки могут использовать для общения с другими, которые могут не иметь понятия о системе типов, с которой они взаимодействуют. Отсутствие функций системы типов более высокого уровня не является идеальным и ограничивает виды вещей, которые вы можете выражать через FFI, но ограничения также являются частью того, почему он настолько успешен в том, что он делает, практически любой язык может найти способ для представления вещей, доступных для него через этот интерфейс, и наоборот.

C ++ решает некоторые из этих проблем (не самая большая проблема - общее адресное пространство), но добавляет ряд концепций, которые на самом деле бесполезны в IPC. Конечно, вы всегда можете использовать надмножество C или подмножество C ++ в качестве IDL, а затем разработать для него правила привязки, но в этот момент вы почти не получаете преимуществ от существующего кода, поэтому вы также можете использовать существующий IDL.

Согласен, для IPC / RPC C - ужасный язык для определения интерфейсов.

Дело в том, что сейчас общий ABI - это кусок байтов, хранящийся в линейной памяти.

Это, конечно, примитив, с которым мы работаем, но C ABI еще многое определяет.

Но в будущем, когда будет реализовано предложение GC, некоторые языки (Java, C #, Python) будут хранить очень мало или ничего в линейной памяти. Вместо этого они будут хранить все свои данные в структурах GC. Если два из этих языков попытаются установить связь, сериализация этих структур в поток байтов только для их немедленной десериализации будет ненужными накладными расходами.

Я не уверен, что эти языки перейдут на передачу GC хосту, но это всего лишь предположение с моей стороны. В любом случае языки, которые понимают управляемые структуры хоста GC, могут просто выбрать общее представление для этих структур с помощью C ABI так же легко, как они могут быть представлены с помощью capnproto, единственная разница в том, где живет спецификация этого представления. Тем не менее, у меня очень слабое представление о деталях предложения GC и о том, как это связано с предложением о привязке хоста, поэтому, если я здесь ошибаюсь, не стесняйтесь игнорировать.

TL; DR: Я думаю, что мы согласны с тем, что касается взаимодействия модулей, где общая линейная память не используется. Но я думаю, что разделяемая память _is_ важна для поддержки, и C ABI - самый разумный выбор для этого варианта использования из-за существующей языковой поддержки. Я надеюсь, что это предложение по мере его развития поддержит оба.

Нам нужен просто максимально эффективный способ обмена буферами байтов и способ согласования языками формата. Нет необходимости исправлять это для одной конкретной системы сериализации. Если Cap'n Proto больше всего подходит для этой цели, он может возникать как обычное стандартное решение по умолчанию, а не по требованию wasm.

Я, конечно, предвзят, так как сделал FlatBuffers , который по эффективности похож на Cap'n Proto, но более гибкий и более широко поддерживаемый. Однако я бы также не рекомендовал использовать этот формат в соответствии с требованиями wasm.

Есть много других форматов, которые могут быть предпочтительнее этих двух в определенных случаях использования.

Обратите внимание, что и Cap'n Proto, и FlatBuffers являются нулевыми копиями, произвольным доступом и эффективны при форматах вложенности (это означает, что формат, завернутый в другой, не менее эффективен, чем не завернутый), которые являются реальными свойствами, которые следует учитывать для межъязыкового взаимодействия. коммуникация. Вы можете представить себе IDL, который позволяет вам определять очень точные байтовые схемы для буфера, включая «следующие байты являются схемой X Cap'n Proto».

Хотя я немного самопродвигаюсь , я могу указать людям на похож на

@aardappel

Нам нужен просто максимально эффективный способ обмена буферами байтов и способ согласования языками формата. Нет необходимости исправлять это для одной конкретной системы сериализации. Если Cap'n Proto больше всего подходит для этой цели, он может возникать как обычное стандартное решение по умолчанию, а не по требованию wasm.

Я понимаю неявную мысль, что wasm не следует использовать как способ навязывать один стандарт другим его конкурентам, и мне лично безразлично, какой IDL будет выбран.

Тем не менее, когда все сказано и сделано, резина в какой-то момент должна соответствовать дороге. Если wasm хочет облегчить межъязыковое общение (что, конечно, не является предположением, которое все разделяют), то ему нужен стандартный формат, который может выражать больше, чем «эти байты составляют числа». Этим форматом может быть capnproto, структуры C, плоские буферы или даже что-то специфическое для wasm, но он не может быть подмножеством всего этого одновременно по причинам, описанным @fgmccabe .

Хотя я тонко самопродвигаюсь, я могу указать людям на FlexBuffers, который похож на FlatBuffers без схемы. Он имеет те же желательные свойства нулевого копирования, произвольного доступа и дешевой вложенности, но может позволить языкам общаться без согласования схемы, без создания кода, аналогично тому, как используется JSON.

Я вижу привлекательность, я не думаю, что это то, чего вы хотите в большинстве случаев при написании библиотеки. Проблема с JSON (помимо ужасного времени синтаксического анализа) заключается в том, что когда вы пишете импорт объекта JSON где-нибудь в своем коде, вы в конечном итоге пишете много дезинфицирующего кода, прежде чем сможете использовать свои данные, например:

assert(myObj.foo);
assert(isJsonObject(myObj.foo));
assert(myObj.foo.bar);
assert(isString(myObj.foo.bar));
loadUrl(myObj.foo.bar);

с потенциальными уязвимостями безопасности, если вы этого не сделаете.

См. Также 6 - Обработка ошибок во время компиляции выше.


@bitwalker

Правильно, я особо не рассматривал возможность разделяемой линейной памяти. Мне нужен кто-то более знакомый с дизайном веб-сборки, чем я ( @lukewagner ?), Чтобы обсудить, насколько это осуществимо и является ли это хорошим способом достижения межмодульных вызовов; это также будет зависеть от того, сколько предположений, на которые опираются FFI, признаны недействительными из-за структуры памяти wasm.

Например, FFI часто полагаются на тот факт, что их основной язык использует библиотеку C, и предоставляют нативным библиотекам доступ к функции malloc напрямую. Насколько хорошо эта стратегия может быть переведена на wasm в контексте двух взаимно подозрительных модулей?

Думаю, я должен сказать что-то в этой теме, как создатель Cap'n Proto, но, как ни странно, я не обнаружил, что у меня есть особенное мнение. Позвольте мне высказать несколько смежных мыслей, которые могут быть интересны, а могут и нет.

Я также являюсь техническим руководителем Cloudflare Workers, «бессерверной» среды, в которой работают JavaScript и WASM.

Мы рассматривали возможность поддержки Cap'n Proto RPC в качестве протокола, позволяющего сотрудникам общаться друг с другом. В настоящее время они ограничены HTTP, поэтому планка установлена ​​довольно низко. :)

В Workers, когда один Worker вызывает другого, очень часто оба работают на одном компьютере, даже в одном процессе. По этой причине сериализация с нулевым копированием, такая как Cap'n Proto, очевидно, имеет большой смысл, особенно для рабочих WASM, поскольку они работают с линейной памятью, которая теоретически может быть физически разделена между ними.

Вторая, менее известная причина, по которой мы считаем, что это хорошо подходит, - это система RPC. Cap'n Proto имеет полный объектный протокол RPC с конвейерной обработкой обещаний, смоделированный по образцу CapTP. Это позволяет легко и безопасно и эффективно выражать насыщенные объектно-ориентированные взаимодействия. Cap'n Proto RPC - это не просто протокол «точка-точка», он скорее моделирует взаимодействие между любым количеством участников сети, что, как мы думаем, будет иметь большое значение.

Тем временем в мире WASM WASI представляет API, основанный на возможностях. Похоже, здесь может быть какая-то интересная «синергия».

С учетом всего вышесказанного, несколько целей дизайна Cap'n Proto могут не иметь смысла для конкретного варианта использования FFI:

  • Сообщения Cap'n Proto разработаны так, чтобы быть независимыми от позиции и непрерывными, чтобы их можно было передавать и совместно использовать между адресными пространствами. Указатели относительны, и все объекты в сообщении должны быть размещены в непрерывной памяти или, по крайней мере, в небольшом количестве сегментов. Это значительно усложняет модель использования по сравнению с собственными объектами. При использовании FFI в одном и том же линейном пространстве памяти эти накладные расходы тратятся впустую, поскольку вы можете передавать собственные указатели для потери объектов кучи.
  • Сообщения Cap'n Proto предназначены для прямой и обратной совместимости между версиями схемы, включая возможность копировать объекты и поддеревья без потерь без знания схемы. Это требует, чтобы некоторая информация о типе света хранилась непосредственно в контенте, который Cap'n Proto кодирует как метаданные для каждого указателя. Если два модуля, взаимодействующие через FFI, компилируются одновременно, то в этих метаданных нет необходимости.
  • Гарантии конвейерной обработки, сокращения пути и упорядочения обещаний Cap'n Proto RPC имеют смысл, когда между вызывающим и вызываемым есть задержка, которой нельзя пренебречь. FFI на одном процессоре не имеет такой задержки, и в этом случае механизм конвейерной обработки обещаний, вероятно, просто тратит циклы.

Короче говоря, я думаю, что когда у вас есть независимо развернутые модули в отдельных песочницах, которые взаимодействуют друг с другом, Cap'n Proto имеет огромное значение. Но для одновременно развернутых модулей в одной песочнице это, вероятно, излишне.

Спасибо за ответ!

Указатели относительны, и все объекты в сообщении должны быть размещены в непрерывной памяти или, по крайней мере, в небольшом количестве сегментов. Это значительно усложняет модель использования по сравнению с собственными объектами. При использовании FFI в одном и том же линейном пространстве памяти эти накладные расходы тратятся впустую, поскольку вы можете передавать собственные указатели для потери объектов кучи.

Я не знаю, насколько возможен подход с общей линейной памятью для wasm (см. Выше).

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

Сообщения Cap'n Proto предназначены для прямой и обратной совместимости между версиями схемы, включая возможность копировать объекты и поддеревья без потерь без знания схемы. [...] Если два модуля, взаимодействующие через FFI, компилируются одновременно, то в этих метаданных нет необходимости.

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

Большим источником бессмысленных накладных расходов может быть то, как capnproto xor s его переменные сравниваются со значениями по умолчанию, что полезно, когда нулевые байты сжимаются, но контрпродуктивно в рабочих процессах с нулевым копированием.

Я не знаю, насколько возможен подход с общей линейной памятью для wasm (см. Выше).

Ах, TBH, я не думаю, что у меня достаточно контекста, чтобы следить за этой частью обсуждения. Если у вас нет общего адресного пространства, тогда да, Cap'n Proto начинает иметь большой смысл.

Я рад дать совет о том, как создавать подобные форматы. FWIW есть несколько мелочей, которые я бы изменил в Cap'n Proto, если бы меня не волновала совместимость с приложениями, которые уже существуют сегодня ... это в основном, вроде бы, детали кодирования указателей низкого уровня.

Большим источником бессмысленных накладных расходов может быть то, как capnproto xors сравнивает свои переменные со значениями по умолчанию, что полезно, когда нулевые байты сжимаются, но контрпродуктивно в рабочих процессах с нулевым копированием.

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

@lukewagner Есть

Я думаю, что есть варианты использования как для совместного использования, так и для отказа от совместного использования линейной памяти, и в конечном итоге инструменты должны поддерживать и то, и другое:

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

Отказ от совместного использования памяти имеет смысл для более слабосвязанной коллекции модулей, где классический дизайн в стиле Unix помещает код в отдельные процессы, соединенные конвейерами. Лично я считаю, что это более захватывающее / футуристическое направление для более композиционной программной экосистемы, и поэтому я выступал за то, чтобы это было по умолчанию для любой инструментальной цепочки, нацеленной на участие в экосистеме ESM / npm через интеграцию ESM (и действительно, что так обстоит дело с wasm-pack / wasm-bindgen в Rust). Использование механизма в непосредственной близости от Web IDL Bindings или предложенного вами расширения имеет для меня большой смысл как форму эффективного, эргономичного, типизированного (синхронизируемого или асинхронного) RPC.

Я, наконец, прочитал это полностью, и это очень похоже на мои мысли в этой области (а это поле для комментариев слишком короткое, чтобы его вместить?).

В частности, я думал о проблеме межмодульного взаимодействия, которую лучше всего описать схемой. То есть нам не нужен формат сериализации Cap'nProto, мы можем просто использовать схему. У меня нет особого мнения о языке схем Cap'nProto в настоящее время.

С точки зрения WASI / ESM + npm решение такой формы для меня наиболее разумно. Это абстракция над ABI, независимо от общего ABI. По сути, он позволяет описывать интерфейс с помощью API-интерфейса языка схемы и вызывать через эти языковые границы с кажущимися нативными интерфейсами ABI на обоих концах, позволяя хосту обрабатывать перевод.

В частности, это не относится к варианту использования для большей координации с другим модулем: если вы точно знаете, что можете совместно использовать ABI, вы фактически можете просто использовать ABI, любой ABI, будь то C или Haskell. Если вы контролируете и компилируете весь рассматриваемый wasm, эту проблему решить намного проще. Только когда вы попадаете в случай npm, когда вы загружаете произвольный неизвестный код и не знаете его исходный язык, что-то вроде взаимодействия на уровне схемы между модулями становится невероятно привлекательным. Потому что мы можем либо использовать ЖК-дисплей самого wasm - который, как я предполагаю, будет следовать той же дуге, что и собственные библиотеки, и использовать C ABI, - либо мы можем использовать ЖК-дисплей языков, закодированных на языке схемы. И схема может быть более гибкой, если сделать требование 2) мягким, например, должна быть возможность эффективно конвертировать из C в Rust в Nim, но из C в Haskell, имеющего больше накладных расходов, не возникает проблем.

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

Я склонен согласиться с первым, но не уверен, что последнее следует. Кто реализует схему? Даже если хост выполняет транспортировку, в какой-то момент вы должны определить, какие значения / байты Wasm фактически потребляются / производятся на обоих концах, и каждый модуль должен приводить свои собственные данные в форму, понятную хосту. Может быть даже доступно несколько форм, но все же это не отличается от формата сериализации, только немного более высокого уровня.

должно быть возможно эффективно преобразовать C в Rust в Nim, C в Haskell, имеющий больше накладных расходов, не является препятствием.

Возможно, нет, но вы должны осознавать последствия. Привлечение C-подобных языков означает, что Haskell не будет использовать эту абстракцию для модулей Haskell из-за накладных расходов. Это, в свою очередь, означает, что он не будет участвовать в той же экосистеме «npm» для своих собственных библиотек.

А «Haskell» здесь просто заменитель практически для всех языков высокого уровня. Подавляющее большинство языков не похожи на Си.

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

Привлечение C-подобных языков означает, что Haskell не будет использовать эту абстракцию для модулей Haskell из-за накладных расходов. Это, в свою очередь, означает, что он не будет участвовать в той же экосистеме «npm» для своих собственных библиотек.

А «Haskell» здесь просто заменитель практически для всех языков высокого уровня. Подавляющее большинство языков не похожи на Си.

Могли бы дать некоторые конкретные варианты использования? В идеале существующие библиотеки на Haskell или другом языке, которые было бы неудобно преобразовать в схему сериализации?

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

@PoignardAzur , их нетрудно перевести, но для этого требуется, чтобы они копировали (сериализовали / десериализовали) все аргументы / результаты на обоих концах каждого межмодульного вызова. Ясно, что вы не хотите платить эту стоимость за каждый вызов внутренней библиотеки языка.

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

Кто реализует схему? Даже если хост выполняет транспортировку, в какой-то момент вы должны определить, какие значения / байты Wasm фактически потребляются / производятся на обоих концах, и каждый модуль должен приводить свои собственные данные в форму, понятную хосту. Может быть даже доступно несколько форм, но все же это не отличается от формата сериализации, только немного более высокого уровня.

Хост реализует схему. Схема вообще не описывает байты, и это может быть деталью реализации. Это заимствовано из дизайна предложения WebIDL Bindings, в котором интересный момент заключается в преобразовании структур C в типы WebIDL. Такой дизайн использует абстрактные типы интерфейса Wasm (я предлагаю аббревиатуру: WAIT) вместо типов WebIDL. В предложении WebIDL мы не нуждаемся или не хотим требовать двоичного представления данных, когда оно «переведено в WebIDL», потому что мы хотим иметь возможность переходить прямо от wasm к API-интерфейсам браузера без промежуточных остановок.

Привлечение C-подобных языков означает, что Haskell не будет использовать эту абстракцию для модулей Haskell из-за накладных расходов.

Ой, согласен на 100%. Я должен был закончить пример, чтобы прояснить это: Между тем, Haskell to Elm to C # может быть столь же эффективным (при условии, что они используют типы wasm gc), но C # to Rust может иметь накладные расходы. Я не думаю, что есть способ избежать накладных расходов при перепрыгивании через языковые парадигмы.

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

Я считаю, что, абстрагируясь от типов и не указывая формат проводов, мы можем дать хостам гораздо больше возможностей для оптимизации. Я думаю, что не целью является сказать «строки в стиле C эффективны», но цель состоит в том, чтобы сказать «языки, которые [хотят] рассуждать о строках в стиле C, могут сделать это эффективно». Или, ни один формат не должен быть благословлен, но определенные совместимые цепочки вызовов должны быть эффективными, и все цепочки вызовов должны быть возможными.

Под цепочками вызовов я подразумеваю:

  1. C -> Rust -> Zig -> Fortran, эффективный
  2. Haskell -> C # -> Haskell, эффективный
  3. C -> Haskell -> Rust -> Схема, неэффективно
  4. Java -> Rust, неэффективно

А «Haskell» здесь просто заменитель практически для всех языков высокого уровня. Подавляющее большинство языков не похожи на Си.

Да, это было моим намерением использовать Haskell как конкретный язык. (Хотя Nim, вероятно, был плохим примером C-подобного языка, потому что он также интенсивно использует GC)

-

Еще один способ, которым я думал об абстрактных типах, - это IR. Точно так же, как LLVM описывает отношение «многие-к-одному-ко-многим» (многие языки -> один IR -> множество целей), абстрактные типы wasm могут опосредовать отображение «многие ко многим», языки + хосты -> языки + хосты. Что-то в этом пространстве дизайна берет проблему отображения N ^ 2 и превращает ее в проблему N + N.

Хост реализует схему.

Что ж, этого недостаточно, каждый модуль должен что-то реализовывать, чтобы хост мог найти данные. Если хост ожидает макет C, вы должны определить этот макет C, и каждый клиент должен маршалировать / демаршалировать его внутри / из него. Это не так уж и отличается от формата сериализации.

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

Что ж, этого недостаточно, каждый модуль должен что-то реализовывать, чтобы хост мог найти данные. Если хост ожидает макет C, вы должны определить этот макет C

Хозяин ничего не должен ожидать , но должен все поддерживать . Более конкретно, используя предложение webidl-bindings в качестве иллюстративного примера , у нас есть utf8-cstr и utf8-str , которые принимают i32 (ptr) и i32 (ptr), i32 (len) соответственно. Нет необходимости указывать в спецификации, что «хост внутренне представляет это как C-строки», чтобы иметь возможность конкретно отображать между ними.
Итак, каждый модуль что-то реализует, да, но представление данных не обязательно должно быть выражено на уровне абстрактных данных / схемы, что дает нам свойство абстрагироваться от этого макета данных.
Кроме того, это расширяется на уровне привязок, который отображает конкретные типы wasm и абстрактные промежуточные типы. Чтобы добавить поддержку Haskell (который моделирует строки как списки минусов символов и массивы символов), мы можем добавить привязки utf8-cons-str и utf8-array-str , которые ожидают (и проверяют) типы wasm (с использованием текущих синтаксис предложения gc) (type $haskellString (struct (field i8) (field (ref $haskellString)))) и (type $haskellText (array i8)) .

Другими словами, каждый модуль решает, откуда поступают данные. Абстрактные типы + привязки позволяют преобразовывать то, как модули рассматривают одни и те же данные, не утверждая, что одно представление является каким-то каноническим.

Формат сериализации для (подмножества) абстрактных типов был бы полезен, но он может быть реализован как потребитель формата схемы, и я считаю, что это ортогональная проблема. Я считаю, что FIDL имеет формат сериализации для подмножества типов, которые могут передаваться по сети, запрещает материализацию непрозрачных дескрипторов, в то же время разрешая непрозрачные дескрипторы для передачи внутри системы (IPC да, RPC нет).

То, что вы описываете, довольно близко к тому, что я имел в виду, с одной большой оговоркой: схема должна иметь небольшое фиксированное количество возможных представлений. Мостовое соединение между различными представлениями - это проблема N * N, что означает, что количество представлений должно быть небольшим, чтобы не перегружать писателей виртуальных машин.

Таким образом, добавление поддержки Haskell потребует использования существующих привязок, а не добавления пользовательских привязок.

Некоторые возможные представления:

  • Структуры и указатели в стиле C.
  • Фактические байты capnproto.
  • GC классы.
  • Замыкания, выступающие в роли геттеров и сеттеров.
  • Словари в стиле Python.

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

Таким образом, добавление поддержки Haskell потребует использования существующих привязок, а не добавления пользовательских привязок.

Зависит от уровня детализации существующих привязок, о которых вы думаете. N <-> N языков, кодирующих каждую возможную привязку, это 2 * N * N, но N <-> IR равно 2 * N, и далее, если вы скажете N <-> [общие стили привязки] <-> IR, где число распространенных форматов - k, вы говорите 2 * k, где k <N.

В частности, со схемой, которую я описываю, вы получаете Scheme бесплатно (она будет повторно использовать utf8-cons-str ). Если Java моделирует строки как массивы символов, это привязка utf8-array-str . Если Ним использует string_views под капотом, utf8-str . Если Zig соответствует C ABI, utf8-cstr . (Я не знаю ABI Java / Nim / Zig, поэтому я не упоминал их в качестве конкретных примеров ранее)

Итак, да, мы не хотим добавлять привязку для каждого возможного языка, но мы можем добавить несколько привязок для каждого типа IR, чтобы охватить подавляющее большинство языков. Я думаю, здесь есть место для разногласий: сколько привязок - это «несколько», что лучше всего, насколько строгими должны быть критерии того, поддерживаем ли мы ABI языка?
У меня нет конкретных ответов на эти вопросы. Я пытаюсь привести множество конкретных примеров, чтобы лучше проиллюстрировать пространство дизайна.

Также я бы сказал, что мы абсолютно хотим указать несколько привязок для каждого абстрактного типа, чтобы избежать привилегий какого-либо одного стиля данных. Если единственная привязка, которую мы предоставляем для Strings, - это utf8-cstr , тогда все языки, не содержащие C-ABI, должны иметь дело с этим несоответствием. Я согласен с увеличением сложности записи виртуальной машины, что немаловажно.
Общий объем работы в экосистеме составляет O (усилия по виртуальной машине + усилия по реализации языка), и оба этих термина каким-то образом масштабируются с N = количеством языков. Пусть M = количество устройств для внедрения, k = количество привязок и a = среднее количество привязок, которое необходимо реализовать для данного языка, с a <= k. Как минимум, у нас есть M + N отдельных реализаций wasm.
Наивный подход, когда каждый N языков независимо реализует ABI FFI со всеми остальными N языками, составляет O (M + N * N). Это то, что мы имеем в собственных системах, и это сильный сигнал о том, что любое значение O (N * N) приведет к результатам, не отличающимся от результатов в собственных системах.
Второй наивный подход, когда каждая виртуальная машина должна реализовать все привязки N * N: O (M * N * N + N), что явно еще хуже.
Мы пытаемся предложить, чтобы у нас было k привязок, которые сопоставляются между абстрактным языком, которые сопоставляются со всеми языками. Это подразумевает k работ для каждой ВМ. Для каждого языка нам нужно реализовать только подмножество привязок. Общая работа равна M * k + N * a, что составляет O (M * k + N * k). Обратите внимание, что в случае, если k = N, сторона виртуальной машины «только» M * N, поэтому для любой данной виртуальной машины она «только» линейна по количеству языков. Очевидно, что мы хотим, чтобы k << N, потому что в противном случае это все равно O (N * N), не лучше, чем первое решение.
Тем не менее, O (M * k + N * k) гораздо вкуснее. Если k равно O (1), это делает всю экосистему линейной по количеству реализаций, что является нашей нижней границей количества задействованных усилий. Более вероятная граница - это O (log (N)), что меня все еще очень устраивает.

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

мы можем добавить несколько привязок для каждого типа IR, чтобы охватить подавляющее большинство языков.

Это ключевое предположение, которое, как я считаю, просто неверно. Мой опыт показывает, что существует (по крайней мере!) Столько же вариантов представления, сколько и языковых реализаций. И они могут быть сколь угодно сложными.

Возьмите V8, в котором только есть несколько десятков (!) Представлений для струн, включая различные кодировки, разнородные связки и т. Д.

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

Другие языки используют забавные представления для длины строки или не хранят ее явно, но требуют ее вычисления.

Эти два примера уже показывают, что декларативный макет данных не сокращает его, вам часто нужно иметь возможность вызывать код времени выполнения, который, в свою очередь, может иметь свои собственные соглашения о вызовах.

И это просто строки, которые концептуально представляют собой довольно простой тип данных. Я даже не хочу думать о бесконечном количестве способов, которыми языки представляют типы продуктов (кортежи / структуры / объекты).

И затем есть принимающая сторона, где вы должны иметь возможность создавать все эти структуры данных!

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

Мой опыт показывает, что существует (по крайней мере!) Столько же вариантов представления, сколько и языковых реализаций. И они могут быть сколь угодно сложными.

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

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

Фактически, вы можете очень далеко продвинуться с помощью всего лишь одного типа данных: пары указатель + len. Тогда вам просто нужна «схема», которая говорит, что находится в этих байтах. Обещает ли он соответствовать UTF-8? Всегда ли последний байт гарантированно равен 0? Являются ли первые 4/8 байтов полями длины / емкости? Могут ли все эти байты быть отправлены прямо в WebGL? Могут ли эти байты быть схемой X существующего формата сериализации? так далее?

Я бы предложил очень простую спецификацию схемы, которая может ответить на все эти вопросы (не существующий формат сериализации, а что-то более низкое, более простое и специфичное для wasm). Затем каждый язык становится бременем для эффективного чтения и записи этих буферов в указанном формате. Слои между ними могут затем обходить буферы вслепую без обработки, либо путем копирования, либо, где это возможно, по ссылке / просмотру.

Это ключевое предположение, которое, как я считаю, просто неверно. Мой опыт показывает, что существует (по крайней мере!) Столько же вариантов представления, сколько и языковых реализаций. И они могут быть сколь угодно сложными.

Я согласен с тем, что это ключевое предположение. Я не согласен с тем, что это неправда, хотя я думаю, что из-за семантического нюанса, который, как мне кажется, я не прояснил.

Привязки не предназначены для отображения на все языковые представления совершенно, им нужно только для отображения на всех языки достаточно хорошо.

Это важнейшее базовое предположение, которое делает его вообще управляемым, независимо от кодировки. Предложение @aardappel пойти в другом направлении и фактически преобразовать байты в декодируемый буфер также построено на предположении, что это кодирование с потерями семантики любой данной программы, некоторые с потерями больше, чем другие.

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

Я действительно забыл об этом, но не думаю, что это имеет значение. Цель состоит не в том, чтобы представить строки Haskell, сохранив при этом все их семантические нюансы за пределами модуля. Цель состоит в том, чтобы преобразовать строку Haskell в строку IR по значению. Это обязательно включает в себя вычисление всей строки.

Эти два примера уже показывают, что декларативный макет данных не сокращает его, вам часто нужно иметь возможность вызывать код времени выполнения, который, в свою очередь, может иметь свои собственные соглашения о вызовах.

Способ смоделировать это, независимо от того, как мы указываем привязки (или даже ЕСЛИ мы указываем что-нибудь для привязок), - это обрабатывать это в пользовательском пространстве. Если языковое представление типа не сопоставляется напрямую с привязкой, его необходимо преобразовать в представление, которое это делает. Например, если строки Haskell действительно представлены как (type $haskellString (struct (field i8) (field (func (result (ref $haskellString)))))) , ему нужно будет либо преобразовать в строгую строку и использовать привязку, подобную схеме, либо в массив Text и использовать привязку, подобную Java, либо к CFFIString и используйте C-подобную привязку. Ценностное предложение наличия нескольких несовершенных типов привязки состоит в том, что некоторые из них менее неудобны для Haskell, чем другие, и можно создавать типы Wasm-FFI без необходимости изменять компилятор.

И это просто строки, которые концептуально представляют собой довольно простой тип данных. Я даже не хочу думать о бесконечном количестве способов, которыми языки представляют типы продуктов (кортежи / структуры / объекты).
И затем есть принимающая сторона, где вы должны иметь возможность создавать все эти структуры данных!

Я сбит с толку, я вижу, что, говоря «связывание между языками совершенно невозможно, мы не должны пытаться вообще», но я считаю, что то, что вы говорили, было больше похоже на «Я не верю описанный здесь подход послушен, «что кажется гораздо более разумным. В частности, мое возражение против этой аргументации состоит в том, что она не описывает путь вперед. Учитывая, что эта проблема «очень сложная», что нам делать?

Вместо этого мы бы начали отдавать предпочтение нескольким

Почти наверняка. Вопрос в том, какова степень привилегии нескольких оптимально поддерживаемых языков? Насколько легко найти эргономичное решение у непривилегированных языков?

при этом уже растет большой зоопарк произвольной хлама.

Я не понимаю, что вы имеете в виду. Моя интерпретация того, что является произвольным, - это «какие языки мы поддерживаем», но это то же самое, что «предоставление привилегий нескольким», что было бы двойным подсчетом. И, таким образом, это было бы фатальным недостатком только на одном уровне, а не на нескольких: D

@aardappel, короткая версия, заключается в том, что это мой план резервного копирования, если декларативный абстрактный подход не сработает: пойти в совершенно противоположном направлении и описать формат сериализации. Было замечено, что сама сеть почти полностью построена на тексте, потому что это чрезвычайно низкий общий знаменатель. Текст легко понять всеми инструментами, поэтому на нем возможно что-то вроде Интернета.

Больше всего меня беспокоят данные в буферах, которые могут сделать этот подход неразрешимым, - это как мы можем обрабатывать ссылочные типы? Лучшая идея, которая у меня есть, - это разделить таблицы и сериализовать в них индексы, но у меня нет полного представления о том, насколько хорошо это будет работать.

@ jgravelle-google, может быть, ссылочные типы следует хранить отдельно? Таким образом, исходная сигнатура данной функции может быть ref ref i32 i32 i32 i32 что на самом деле представляет собой 2 ссылки anyref, за которыми следуют 2 буфера определенного типа (указанного в гипотетической схеме выше).

(Между прочим, я не знаком с Haskell, но идея строки как ленивых списков символов взорвала меня. Когда связаны списки байтов, когда-либо самый эффективный или удобный способ сделать что-либо ? Я понимаю, что Haskell нужно все, чтобы быть неизменным, а связанные списки допускают дешевое добавление в начало, но вы можете получать и манипулировать неизменяемыми строками без использования связанных списков.)

Больше всего меня беспокоят данные в буферах, которые могут сделать этот подход неразрешимым, - это как мы можем обрабатывать ссылочные типы? Лучшая идея, которая у меня есть, - это разделить таблицы и сериализовать в них индексы, но у меня нет полного представления о том, насколько хорошо это будет работать.

Это одна из причин, по которой я предложил capnproto в качестве кодировки. Справочные таблицы более или менее встроены.

В любом случае мы бы хотели, чтобы любой выбранный нами формат имел ссылочные типы как первоклассные граждане, которые можно было бы разместить в любом месте графа данных. (например, в опциях, массивах, вариантах и ​​т. д.)

Спасибо всем за ваш отзыв.

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

Подводя итоги полученной обратной связи:

  • Нет единого мнения о том, какой формат сериализации, если таковой имеется, лучше всего подходит для wasm. Альтернативы включают FlatBuffers, структурные графы C ABI с необработанными указателями, индивидуальный формат wasm IDL или некоторую комбинацию вышеперечисленного.

  • Предложение требует более сильного негативного пространства. Множество читателей были сбиты с толку объемом предложения и вариантами использования, для которых оно предназначалось (статическое или динамическое связывание, от модуля к хосту против хоста к хосту, совместное использование изменяемых данных против передачи неизменяемых сообщений).

  • @lukewagner выразил некоторый энтузиазм по поводу потенциала модульной системы, объединяющей взаимно недоверчивые модули, в сочетании с интеграцией ESM. Следующая итерация предложения должна расширить этот потенциал; в частности, я считаю, что наличие обратно совместимой системы типов позволит wasm использовать модель дерева зависимостей, подобную npm, избегая при этом основной тяжести проблемы ромба зависимостей.

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

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

    • Слишком много внимания уделяется наиболее заметным языкам, оставляя больше нишевых языков позади.
    • Наличие дырявой абстракции, которая пытается быть слишком общей, но не охватывает чьи-либо варианты использования удобно или эффективно.
    • Покрытие слишком большого числа случаев, создание раздутой реализации N * N, которая все еще страдает от вышеуказанных проблем.

Следующая итерация предложения должна так или иначе решить эти проблемы.

В частности, я думаю, что дискуссии будут полезны некоторые соломенные примеры, вокруг которых можно рассуждать. Эти примеры будут включать как минимум две библиотеки, написанные на разных языках с разным расположением данных (например, C ++ и Java), которые будут использоваться как минимум двумя минимальными программами на разных языках (например, Rust и Python), чтобы проиллюстрировать проблему n * n и стратегии для ее решения.

Кроме того, как отметили читатели, предложение в настоящее время связывает идею схемы типов с идеей ее представления. Несмотря на то, что в предложении достаточно хорошо изложены требования к формату представления, сначала необходимо изложить требования схемы абстрактного типа.

В любом случае, еще раз спасибо всем, кто до сих пор участвовал в этой дискуссии. Я постараюсь как можно скорее предложить более подробное предложение. Если кто-то здесь заинтересован в том, чтобы помочь мне написать его, обязательно напишите мне по электронной почте!

@ jgravelle-google:

Способ смоделировать это, независимо от того, как мы указываем привязки (или даже ЕСЛИ мы указываем что-нибудь для привязок), - это обрабатывать это в пользовательском пространстве.

Да, я согласен, и мой аргумент аналогичен аргументу @aardappel : если это то, что мы обычно должны делать в любом случае, мы должны просто принять это и не пробовать специальные вещи для улучшения некоторых странных случаев. Userland - это место преобразования, в духе всего остального в Wasm.

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

Я думаю, что совершенно желательно определить DDL (схему типов) для взаимодействия данных между языками. Я просто не думаю, что конвертировать Wasm можно с легкостью. Преобразования должны быть реализованы в пользовательском пространстве. Уровень привязки просто предписывает формат, который пользовательский код должен создавать / использовать.

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

Извините, я имел в виду, что подозреваю, что в этих преобразованиях не будет ничего каноничного. Таким образом, и их выбор «произвольный», и их индивидуальная семантика.

как мы можем обрабатывать ссылочные типы?

Ах, это хороший вопрос. FWIW, мы пытаемся решить эту проблему прямо сейчас для IDL / DDL для платформы Dfinity. Пока есть только anyref, решение довольно простое: формат сериализации определяет две части: срез памяти, проецирующий прозрачные данные, и срез таблицы, проецирующий содержащиеся ссылки. Множественные ссылочные типы требуют соответственно нескольких срезов таблицы. Сложный вопрос заключается в том, что делать, когда набор типов ссылок больше не является конечным (например, с типизированными ссылками на функции).

Когда у нас есть типы GC, должен быть альтернативный способ предоставления данных, который представляет собой значение GC. В этом случае ссылки не являются проблемой, потому что их можно свободно смешивать.

@PoignardAzur :

Кстати, я не знаком с Haskell, но идея строки как ленивых списков символов поразила меня.

Да, я считаю, что в наши дни это широко считается ошибкой. Но он демонстрирует, насколько разнообразны даже «простые» типы данных.

@rossberg

Я думаю, что совершенно желательно определить DDL (схему типов) для взаимодействия данных между языками. Я просто не думаю, что конвертировать Wasm можно с легкостью. Преобразования должны быть реализованы в пользовательском пространстве.

Я согласен и добавлю к этому: я скептически отношусь к добавлению чего-либо в спецификацию wasm для этого, потому что я не думаю, что wasm больше нуждается в межъязыковом решении, чем другие платформы, и я не думаю, что wasm имеет больше возможностей для реализации такого решения, чем другие платформы. Для меня здесь нет ничего особенного в wasm, поэтому я не уверен, почему мы можем добиться большего, чем стандартные решения в этой области, например, буферы, о которых упоминал

Одна особенная вещь wasm, по крайней мере, в Интернете, - это типы JavaScript / Web API для строк, массивов и так далее. Очевидно, важно иметь возможность взаимодействовать с ними.

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

Я думаю, да. Использование в Интернете по умолчанию означает, что код может и будет выполняться в разных контекстах. Точно так же, как можно было бы <script src="http://some.other.site/jquery.js"> , я хотел бы видеть людей, объединяющих библиотеки wasm с перекрестным происхождением. Из-за свойств эфемерности и компоновки, которые предоставляет Интернет, добавленная стоимость возможности взаимодействия с внешним модулем выше, чем когда-либо в собственных системах.

и я не думаю, что у wasm больше возможностей реализовать такое решение, чем на других платформах.

И я думаю, что это так. Поскольку wasm запускается устройством для внедрения / на хосте, генерация кода эффективно абстрагируется. Из-за этого виртуальная машина имеет гораздо больше инструментов + свободу действий для поддержки конструкций более высокого уровня, которые невозможны в собственных системах.

Поэтому я думаю, что что-то в этой области более ценно и более возможно, чем в других системах, поэтому wasm является особенным в этом контексте. Для меня JS-взаимодействие - это частный случай более общего представления о том, что модули wasm должны иметь возможность общаться с внешними объектами с очень разными взглядами на мир.

Дальнейший путь для этого состоит в том, чтобы на данный момент полностью перейти к взаимодействию на уровне инструментов и отложить стандартизацию до тех пор, пока у нас не будет выигрышного формата. Итак, если цель состоит в том, чтобы преобладающая экосистема менеджера пакетов wasm использовала заданный формат интерфейса (и это NPM, или WAPM, или какой-то еще созданный менеджер пакетов?), То это может произойти независимо от стандартизации. Теоретически мы можем стандартизировать то, что люди уже делают, чтобы повысить производительность, но эргономика может быть реализована на уровне пользователя. Риск заключается в том, что победивший межъязыковой формат не поддается оптимизации, и мы получаем субоптимальный стандарт де-факто. Если мы сможем разработать формат с намерением стандартизации позже (декларативного стиля в настраиваемом разделе в основном достаточно?), Это устранит этот риск, но также задержит любое улучшение производительности. Для меня производительность - один из менее захватывающих мотивов, побуждающих заниматься подобными вещами, поэтому я вполне согласен с этим, хотя другие могут не согласиться.

(и это NPM или WAPM или какой-то еще созданный менеджер пакетов?)

Я думаю, что для WAPM еще слишком рано стать жизнеспособным менеджером пакетов. Нам нужны такие функции, как интеграция ESM, WASI и некоторая форма межъязыковых привязок, которые должны быть стандартизованы, прежде чем станет возможным менеджер пакетов wasm.

Как бы то ни было, я не думаю, что в WAPM есть даже управление зависимостями.

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

Я думаю, да. Использование в Интернете по умолчанию означает, что код может и будет выполняться в разных контекстах. Точно так же, как можно было бы

Преобразования должны быть реализованы в пользовательском пространстве. Уровень привязки просто предписывает формат, который пользовательский код должен создавать / использовать.

Для меня это хорошее резюме.

Пока я пишу новый черновик, у меня есть открытый вопрос ко всем в этой ветке:

Есть ли какая-то существующая библиотека, которую, если бы она была скомпилирована для WebAssembly, вы бы хотели использовать с любого языка?

По сути, я ищу возможные варианты использования, на которых можно основывать дизайн. У меня есть свои собственные (в частности, React, движок Bullet и системы плагинов), но мне бы хотелось больше примеров для работы.

@PoignardAzur Многие языки в C используют одни и те же библиотеки Perl-Compatible Regular Expression (PCRE), но при встраивании браузера они, вероятно, должны использовать JS Regex API.

На ум приходят

Также реализация Cap'n Proto RPC, но это странно: слой _serialization_ Cap'n Proto реально должен быть реализован независимо на каждом языке, поскольку большая часть его представляет собой широкий, но неглубокий уровень API, который должен быть идиоматическим и встроенным. -дружелюбный. Слой RPC, OTOH, узкий, но глубокий. В принципе, должно быть возможно использовать реализацию C ++ RPC за реализацией сериализации любого произвольного языка, передавая ссылки на байтовые массивы в кодировке capnp через границу FFI ...

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

Я хотел бы отметить, что у мира SmallTalk был некоторый положительный опыт с такими усилиями, которые могут быть информативными при разработке их State Replication Protocol (SRP), который является эффективным протоколом сериализации, который может довольно эффективно представлять любой тип любого размера. Я подумал о том, чтобы сделать это встроенным макетом памяти для виртуальной машины или даже FPGA, но не дошел до того, чтобы попробовать. Я знаю, что его портировали по крайней мере на один другой язык, Squeak, с хорошими результатами. Конечно, есть что почитать, поскольку оно сильно пересекается с проблемами, проблемами и опытом, связанными с этим предложением.

Я понимаю, почему Web IDL был предложением по умолчанию в качестве связывающего языка: это исторический и в некотором роде зрелый язык связывания для Web. Я очень поддерживаю это решение, и, скорее всего, я бы так же и поступил. Тем не менее, мы можем признать, что он едва ли подходит для других контекстов (понимаете, других хостов / языков). Wasm разработан как независимый от хоста или языка / платформы. Мне нравится идея использования зрелых веб-технологий и поиска вариантов использования для сценариев, не связанных с сетью, но в случае веб-IDL она кажется действительно привязанной к сети. Вот почему я очень внимательно слежу за этими разговорами здесь.

Я открыл https://github.com/WebAssembly/webidl-bindings/issues/40 , что побудило меня задать вопрос здесь, поскольку я не видел упоминания об этом (или я его пропустил).

Во всей истории привязки неясно, «кто» отвечает за создание привязок:

  • Это компилятор (который преобразует программу в модуль Wasm)?
  • Это автор программы (а значит, привязки написаны от руки)?

Я думаю, что оба варианта верны. А в случае Web IDL, похоже, есть некоторые ограничения (см. Ссылку выше). Возможно, я просто пропустил важный шаг в этом процессе, и поэтому подумайте о том, чтобы забыть свое сообщение.

Даже если цель состоит в том, чтобы «переориентировать» Web IDL, чтобы он стал менее веб-ориентированным, прямо сейчас он _is_ очень веб-ориентирован. И возникают предложения предложить альтернативы, отсюда и эта ветка. Поэтому меня беспокоит потенциальная фрагментация. В идеале (а именно так был разработан Wasm), учитывая модуль Wasm, включая его привязки, его можно запускать где угодно как есть. С привязками, написанными на Web IDL, Cap'n 'Proto, FlatBuffers и т. Д., Я почти уверен, что не все компиляторы или авторы программ будут писать одни и те же привязки в разных синтаксисах, чтобы быть действительно кроссплатформенными. Как ни странно, это аргумент в пользу рукописных привязок: люди могут внести свой вклад в программу, написав привязки для платформы P. Но давайте признаем, что это совсем не идеально.

Итак, подведем итоги: меня беспокоит возможная фрагментация между веб-привязками и привязками не-веб. Если поддерживается язык, не связанный с веб-привязкой, будет ли он реально реализован веб-браузерами? Им пришлось бы написать привязки «Wasm ⟶ язык привязки B ⟶ Web IDL». Обратите внимание, что это один и тот же сценарий для всех хостов: Wasm ⟶ язык привязки B ⟶ API хоста.

Для тех , кто интересуется, я работаю в Васмере и я автор PHP- , Python- , рубиновые и Перейти- интеграций Wasm. У нас появляется хорошая площадка для взлома разных привязок для самых разных хостов. Если кто-то хочет, чтобы я интегрировал различные решения, собирал отзывы или пытался экспериментировать, мы все открыты и готовы сотрудничать и вкладывать в это больше ресурсов.

Текущее направление в привязках webIDL, вероятно, далеко от webIDL.
сам. Однако дилемма такова:

`` Естественный '' язык для выражения межмодульного и хост-модуля
функциональная совместимость значительно богаче естественного языка WASM. Этот
означает, что любой пригодный к использованию IDL-эквивалент будет выглядеть довольно произвольно из
WASM от первого лица.

С другой стороны, для людей, которые смотрят на мир через призму C / C ++
(и Rust и ему подобные) все, что богаче модели WASM, рискует оказаться
непригодный для использования. Мы уже можем видеть это с трудностью интегрирования ref
типов в цепочку инструментов.

Кроме того, в обязанности WASM не входит поддержка общих
межъязыковая совместимость. И это не должно быть ИМО.

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

Итог: есть случай, когда есть IDL-эквивалент, и он нам нужен
для поддержки взаимодействия через границы владения. Чем это закончится
на данный момент неясно.

В понедельник, 24 июня 2019 г., в 7:02 Иван Эндерлин [email protected]
написал:

Я понимаю, почему Web IDL был предложением по умолчанию в качестве обязательного языка:
Это исторический и в какой-то мере зрелый связывающий язык для Интернета. я
сильно поддерживаю это решение, и вполне вероятно, что я бы сделал
тем же. Тем не менее, мы можем признать, что это едва ли подходит для других контекстов.
(понимаю, другие хосты / языки). Wasm не зависит от хоста,
или не зависит от языка / платформы. Мне нравится идея использовать зрелую сеть
технологий и найти вариант использования для не-веб-сценариев, но в случае
Web IDL, похоже, действительно привязан к Интернету. Вот почему я очень
внимательно слежу за этими разговорами здесь.

Я открыл WebAssembly / webidl-bindings # 40
https://github.com/WebAssembly/webidl-bindings/issues/40 , что привело меня
чтобы задать вопрос здесь, так как я не видел упоминания об этом (или я его пропустил).

Во всей обязывающей истории неясно, «кто» несет ответственность перед
сгенерируйте привязки:

  • Это компилятор (который преобразует программу в модуль Wasm)?
  • Это автор программы (а значит, привязки написаны от руки)?

Я думаю, что оба варианта верны. А в случае Web IDL, похоже,
ограничения (см. ссылку выше). Может я просто пропустил важный шаг в
процесс, так вот, подумайте о том, чтобы забыть мое сообщение.

Даже если цель состоит в том, чтобы «переориентировать» Web IDL, чтобы он был менее веб-ориентированным, прямо сейчас,
это очень Web-ориентированные. И возникают предложения предложить альтернативы,
отсюда и эта ветка. Поэтому меня беспокоит потенциальная фрагментация.
В идеале (а именно так был разработан Wasm) с учетом модуля Wasm
включая его привязки, его можно запускать где угодно как есть. С участием
привязки, написанные на Web IDL, Cap'n 'Proto, FlatBuffers и т. д., я
уверен, что не все компиляторы или авторы программ напишут одно и то же
привязки в разных синтаксисах, чтобы быть действительно кроссплатформенными. Как ни странно, это
аргумент в пользу рукописных привязок: люди могут внести свой вклад в
программу, написав привязки для платформы P. Но давайте признаем, что это не будет
вообще идеал.

Итак, подведем итоги: меня беспокоит возможная фрагментация между Интернетом и
не-веб-привязки. Если используется язык, не связанный с веб-привязкой, будет ли он
реально реализовано веб-браузерами? Они должны были бы написать
bindings «Wasm язык привязки B Web IDL». Обратите внимание, что это то же самое
сценарий для всех хостов: Wasm ⟶ язык привязки B ⟶ host API.

Для тех, кому интересно, я работаю в Wasmer и являюсь автором PHP-
https://github.com/wasmerio/php-ext-wasm , Python-
https://github.com/wasmerio/python-ext-wasm , Ruby-
https://github.com/wasmerio/ruby-ext-wasm и Go-
https://github.com/wasmerio/go-ext-wasm Интеграции Wasm. Мы начинаем
иметь хорошую площадку для взлома разных привязок для самых разных
хосты. Если кто-то хочет, чтобы я интегрировал разные решения, собирал
отзывы, или попробуйте поэкспериментировать, мы все открыты и готовы к сотрудничеству
и вложить в него больше ресурсов.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/WebAssembly/design/issues/1274?email_source=notifications&email_token=AAQAXUD6WA22DDUS7PYQ6F3P4DHYRA5CNFSM4HJUHVG2YY3PNVWWK3TUL52HS443LVMVREXDNVWWK3TUL52HS443DFMVREXDNVWWK3TUL52HS443DFMVREX5XWWWWG08C02HB2HB0B0B0B0B0
или отключить поток
https://github.com/notifications/unsubscribe-auth/AAQAXUGM66AWN7ZCIVBTXVTP4DHYRANCNFSM4HJUHVGQ
.

-
Фрэнсис МакКейб
SWE

Мы уже видим это на примере сложности интеграции типов ссылок в цепочку инструментов.

Я не могу говорить на других языках, но Rust + wasm-bindgen уже поддерживает:

Мне любопытно: какие трудности вы имеете в виду?

Я понимаю трудности, связанные с C ++. Rust имеет достаточно мощное метапрограммирование, чтобы сделать это более разумным с точки зрения языка, но пользовательскому C ++, например, труднее рассуждать о каких-либо ссылках.

Мне было бы любопытно услышать здесь больше о специфических проблемах C ++. (Они специфичны для C ++ или для LLVM?)

C ++ не знает, что такое тип ссылки. Таким образом, вы не можете иметь его внутри
произвольный объект. Не совсем часть языка; больше похоже на файл
дескриптор. Забавное место для веревочки.

В понедельник, 24 июня 2019 г., в 15:07 Алон Закай [email protected] написал:

Мне было бы любопытно услышать здесь больше о специфических проблемах C ++. (Они
Для C ++ или для LLVM?)

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/WebAssembly/design/issues/1274?email_source=notifications&email_token=AAQAXUDW237MUBBUUJLKS6LP4FARJA5CNFSM4HJUHVG2YY3PNVWWK3TUL52HS4DFMVREXG74
или отключить поток
https://github.com/notifications/unsubscribe-auth/AAQAXUB4O3ZX4LRQSQRL763P4FARJANCNFSM4HJUHVGQ
.

-
Фрэнсис МакКейб
SWE

Говоря с @fgmccabe в автономном режиме, это правда, что и C ++, и Rust не могут напрямую хранить тип ссылки в структуре, поскольку структура будет храниться в линейной памяти. И C ++, и Rust, конечно, могут обрабатывать типы ссылок косвенно, точно так же, как они обрабатывают дескрипторы файлов, текстуры OpenGL и т. Д. - с целочисленными дескрипторами. Я думаю, что его точка зрения заключается в том, что ни один из этих двух языков не может обрабатывать типы ссылок "хорошо" / "изначально" (поправьте меня, если я ошибаюсь!), С чем я согласен - эти языки всегда будут в невыгодном положении по типу ссылок. операций, по сравнению с языками GC.

Мне по-прежнему интересно, есть ли здесь что-нибудь специфическое для C ++. Я не думаю, что есть?

Я понимаю, что делает C ++ трудным, если вы скажете:

struct Anyref; // opaque declaration
void console_log(Anyref* item); // declaration of ref-taking external JS API
Anyref* document_getElementById(const char* str);

void wellBehaved() {
  // This should work
  Anyref* elem = document_getElementById("foo");
  console_log(elem);
}

void notSoWellBehaved() {
  // ????
  Anyref* elem = document_getElementById("bar");
  Anyref* what = (Anyref*)((unsigned int)elem + 1);
  console_log(what);
}

Хорошая новость заключается в том, что последний пример - это UB, я считаю (недействительные указатели становятся UB, как только они создаются), но как мы попытаемся смоделировать это в LLVM IR?

@ jgravelle-google Я думаю, что даже struct Anyref; предполагает, что это имеет смысл в линейной памяти. Вместо этого, почему бы не смоделировать его с помощью целочисленного дескриптора, как упоминалось ранее, в виде текстур OpenGL, дескрипторов файлов и т. Д.?

using Anyref = uint32_t; // handle declaration
void console_log(Anyref item); // declaration of ref-taking external JS API
Anyref document_getElementById(const char* str);

void wellBehaved() {
  // This should work
  Anyref elem = document_getElementById("foo");
  console_log(elem);
}

Целочисленный дескриптор необходимо искать в таблице, когда он будет использоваться - опять же, это всего лишь обратная сторона языков, использующих линейную память, таких как C ++ и Rust. Однако его определенно можно оптимизировать хотя бы локально - если не с помощью LLVM, то на уровне wasm.

Это сработает, но тогда вам нужно обязательно вызвать table_free(elem) иначе вы сохраните ссылку на него навсегда. Что, конечно, не странно для C ++.

Это что-то вроде странного отображения, потому что, я думаю, оно не очень хорошо наслоено? Как будто это похоже на библиотеку а-ля OpenGL, но она полагается на магию компилятора, чтобы предоставить - я не думаю, что вы можете создать anyref.h на C ++ даже с inline-wasm, если вы зависите от объявления отдельного стол.

В любом случае, я думаю, что все это выполнимо / выполнимо, но не все так просто.

@kripken Верно, что "правильная" нативная поддержка anyref потребует некоторых изменений в LLVM (и в rustc), но на самом деле это не препятствие.

wasm-bindgen сохраняет подлинные wasm anyref s в таблице wasm, а затем в линейной памяти сохраняет в таблице целочисленный индекс. Таким образом, он может затем получить доступ к anyref с помощью инструкции wasm table.get .

Пока не будет реализован wasm-gc, языки сборки мусора должны будут использовать ту же самую стратегию, поэтому Rust (и др.) Не упускает из виду.

Так что же нам даст встроенная поддержка anyref в LLVM? Что ж, это позволит напрямую передавать / возвращать anyref из функций, вместо того, чтобы косвенно передавать anyref через таблицу wasm. Да, это было бы полезно, но это всего лишь оптимизация производительности, на самом деле это не мешает использованию anyref .

@Pauan

wasm-bindgen сохраняет подлинные ссылки wasm anyref в таблице wasm, а затем в линейной памяти сохраняет в таблице целочисленный индекс. Таким образом, он может получить доступ к anyref с помощью инструкции wasm table.get.

Совершенно верно, это та модель, о которой я имел в виду.

Пока не будет реализован wasm-gc, языки сборки мусора должны будут использовать ту же самую стратегию, поэтому Rust (и др.) Не упускает из виду.

Да, сейчас языки GC не имеют преимуществ, потому что у нас нет собственного wasm GC. Но, надеюсь, это изменится! :) В конце концов, я ожидаю, что языки GC будут иметь здесь явное преимущество, по крайней мере, если мы сделаем GC должным образом.

Так что же нам даст встроенная поддержка anyref в LLVM? Что ж, это позволит напрямую передавать / возвращать anyref из функций, вместо того, чтобы косвенно использовать anyref через таблицу wasm. Да, это было бы полезно, но это всего лишь оптимизация производительности, на самом деле это не мешает использованию anyref.

Согласен, да, это будет только преимущество языков GC (в конечном итоге) перед C ++ и Rust и т. Д. Это не мешает использованию.

Однако циклы представляют собой большую проблему для C ++ и Rust, поскольку таблицы являются корневыми. Может быть, у нас может быть API трассировки или «теневые объекты», по сути, некоторый способ сопоставить структуру ссылок GC внутри C ++ / Rust, чтобы внешний GC мог их понять. Но я не думаю, что есть настоящее предложение по любому из них.

В конце концов, я ожидаю, что языки с GC будут иметь здесь явное преимущество, по крайней мере, если мы сделаем GC должным образом.

Я могу ошибаться, но я был бы удивлен, если бы это было так: языки GC должны были бы выделить структуру wasm GC, а затем движок wasm должен был бы отслеживать это, когда он проходит через программу.

Для сравнения, Rust не требует выделения (просто присваивается таблице) и должен хранить только целое число, а движку wasm нужно отслеживать только 1 статическую неподвижную таблицу для целей сборки мусора.

Я полагаю, что доступ к anyref может быть оптимизирован для языков GC, поскольку для этого не нужно использовать table.get , однако я ожидаю, что table.get будет довольно быстрым.

Не могли бы вы подробнее объяснить, как вы ожидаете, что программа wasm-gc будет работать лучше, чем программа, использующая таблицу wasm?

PS Это начинает уходить далеко от темы, так что, может быть, нам стоит перенести это обсуждение в новую ветку?

На самом деле именно это: избегать table.get/table.set . С GC у вас должен быть необработанный указатель прямо здесь, сохраняя косвенные ссылки. Но да, вы правы, что Rust и C ++ должны хранить только целые числа, и в целом они довольно быстрые, поэтому любое преимущество GC может не иметь значения!

Я согласен, что мы можем отклониться от темы, да. Я думаю, что по теме есть точка зрения @fgmccabe о том, что

@kentonv

Слой сериализации Cap'n Proto реально должен быть реализован независимо на каждом языке, поскольку большая часть его представляет собой широкий, но неглубокий уровень API, который должен быть идиоматическим и встроенным. Слой RPC, OTOH, узкий, но глубокий

Какая это папка?

@PoignardAzur Извините, я не понимаю ваш вопрос.

@kentonv Я просматриваю репозиторий capnproto на Github. Где уровень сериализации?

@PoignardAzur Итак, это снова моя точка зрения. На самом деле нет ни одного места, на которое можно было бы указать и сказать «это уровень сериализации». По большей части «сериализация» Cap'n Proto - это просто арифметика указателя при загрузке / сохранении в нижележащий буфер. Имея файл схемы, вы используете генератор кода для создания файла заголовка, который определяет встроенные методы, которые выполняют правильную арифметику указателя для определенных полей, определенных в схеме. Затем код приложения должен вызывать эти сгенерированные средства доступа каждый раз, когда он читает или записывает какое-либо поле.

Вот почему не имеет смысла вызывать реализацию, написанную на другом языке. Использование необработанного FFI для каждого доступа к полю было бы чрезвычайно обременительным, поэтому, без сомнения, вы в конечном итоге написали бы генератор кода, который обертывает FFI во что-то более красивое (и специфичное для вашей схемы). Но этот сгенерированный код будет по крайней мере таким же сложным, как код, который уже реализует Cap'n Proto - возможно, более сложным (и намного медленнее!). Поэтому имеет смысл написать генератор кода напрямую для целевого языка.

Возможно, есть некоторые внутренние вспомогательные функции в реализации Cap'n Proto, которые можно использовать совместно. В частности, layout.c++ / layout.h содержит весь код, который интерпретирует кодировку указателя capnp, выполняет проверку границ и т. Д. Сгенерированные средства доступа вызывают этот код при чтении / записи полей указателя. Так что я мог бы, может быть, представить, как обернуть эту часть в FFI, который будет вызываться из нескольких языков; но я бы все равно ожидал написать генераторы кода и некоторое количество библиотеки поддержки времени выполнения на каждом целевом языке.

Ага, извините, я имел в виду обратное ^^ (слой RPC)

@PoignardAzur Ооо , и я полагаю, вам особенно интересно посмотреть на интерфейсы, поскольку вы думаете о том, как обернуть их в FFI. Итак, вы хотите:

  • capability.h : абстрактные интерфейсы для представления возможностей и вызова RPC, которые теоретически могут поддерживаться различными реализациями. (Это самая важная часть.)
  • rpc.h : Реализация RPC по сети.
  • rpc-twoparty.h : Транспортный адаптер для RPC через простое соединение.

Это предложение теперь заменено # 1291: привязки OCAP.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги