MaxGraey
> Вон разработчики MirageOS хвалятся, что их runtime загружается на холодной виртуалке за время ответа на DNS-запрос.
Так парадигма у OCaml не прибита гвоздями, это мультипарадигмальный язык программирования с одним из самых быстрых компиляторов среди ML додобных языком. Да на Ocaml можно написать довольно низкоуровневые приложения если у разработчика прямые руки, кто же спорит? Только там вряд ли будет чистый FP.
> Возможность обойтись без GC тоже не очевидна. Чтобы на Си/Си++ писать относительно безопасно, без фрагментации памяти, нужны аппаратная подсистема виртуальной памяти
Rust отлично обходиться без GC и ARC (ну почти). Насчет фрагментации - это уже удел аллокатора а не высокоуровневого менеджера памяти.
> Из этого всего: чёрт его знет, что в runtime тащит больше зависимостей. Экосистема ФП или экосистема Си
В Rust есть такое поняти как zero-cost abstactions. То есть, если что то ты не используешь - это не будет включено в рантайм вообще, а если используешь, то это будет максимально развернуто, заинлайнено и частично эвалюировано, там даже есть MIR (middleware) для этого, только пока он работает не на полную мощь. Добавь сюда параметрический полиморфизм и отсутсвие/минимизация динамической диспечеризации как таковой (нет vtable "поправка - есть" или thunks). А теперь посчитай сколько всего нужно для рантайма FP на примере Ocaml:
- GC
- OCAMLLIB в которую водит работа с bigint (Polymorphic variants) и пр
Самое лучшее что удавалось добиться судя по комментариям на SO это 200 KB для минимального приложения против 10 КВ в среднем для такой же программы на C
"Так парадигма у OCaml не прибита гвоздями, это мультипарадигмальный язык программирования с одним из самых быстрых компиляторов среди ML додобных языком."
Но в OCaml есть полноценная поддержка FP, функции в нём обвешаны всеми необходимыми для этого механизмами, более сложными, чем необходимые для чистого FP, потому что нужно поддерживать в замыканиях объекты и изменяемые переменные. Но компилятор справляется и выдаёт эффективный код, а runtime не такой уж и громоздкий, даже с полноценным сетевым стеком.
"Rust отлично обходиться без GC и ARC (ну почти). Насчет фрагментации - это уже удел аллокатора а не высокоуровневого менеджера памяти."
Проблему дефрагментации нельзя решить только на уровне аллокатора. Нужно при перемещении блоков в памяти менять живые указатели на стеке и в регистрах, а если язык не предоставляет в runtime никаких способов отличать указатели от других значений, то такая дефрагментация будет весьма ресурсоёмкой. Эта проблема ортогональна способу определения живых объектов. Без поддержки со стороны языка можно сделать всё, конечно, через косвенную адресацию, но придётся платить за каждый доступ.
"В Rust есть такое поняти как zero-cost abstactions. То есть, если что то ты не используешь - это не будет включено в рантайм вообще, а если используешь, то это будет максимально развернуто, заинлайнено и частично эвалюировано, там даже есть MIR (middleware) для этого, только пока он работает не на полную мощь. Добавь сюда параметрический полиморфизм и отсутсвие/минимизация динамической диспечеризации как таковой (нет vtable "поправка - есть" или thunks)."
Я не знаю в деталях, как Rust устроен, но из общеизвестных соображений, параметрический полиморфизм с мономорфизацией кода (это когда всё разрешается статически и инлайнится) приводит к экспоненциальному (от количества типов) росту объёмов кода. Это не подходит для embedded-приложений. А в обычных условиях создаёт нагрузку на кэши и внутренние буферы микроопераций. Поэтому so so. Не очевидно, что делать так быстрее. Видел случаи, когда интерпретаторы языка J работали гораздо быстрее аналогичного Си++ кода по этой причине.
"Самое лучшее что удавалось добиться судя по комментариям на SO это 200 KB для минимального приложения против 10 КВ в среднем для такой же программы на C"
ML может с этими 200KB работать на голом железе. А 10KB Си не смогут. Runtime полноценного Си - это, в том числе, POSIX. И для Rust тоже это верно. Не очень понятно, как Rust может работать без поддержки операционки, особенно в управлении памятью. Конечно, можно написать операционку на Rust, но каков будет её размер? Я боюсь, что из-за проверки заимствований, код придётся делать более громоздким, потому что сложно будет пользоваться обычными для ядер операционок структурами данных, которые очень-очень-очень shared и dynamic.
Не изучал этот вопрос. Есть какие-нибудь непримитивные примеры ядер на Rust?