LINUX.ORG.RU

Man or boy 2к25: Ваш статически типизированный ЯП полноценен?

 


0

4

Когда то Кнут придумал тест для ALGOL реализаций, и он известен под именем «Man or boy test». Но там просто локальные функции, не особо интересно.

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

Для начала нарушу это правило (у Python динамическая типизация), и покажу Python версию:

def print_sum(x):
  def make(acc):
    def f(y):
      print("acc(%d) + %d" % (acc, y))
      return make (acc + y)
    return f
  return make(x)

print_sum(10)(20)(30)(40)
Вывод
acc(10) + 20
acc(30) + 30
acc(60) + 40

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

Мое повторение на OCaml с rectypes:

let print_sum x =
  let rec f acc = fun y ->
    printf "acc(%d) + %d\n" acc y;
    f (acc + y)
  in 
  f x
  
let () = ignore (print_sum 10 20 30 40)
Типы он вывел сам, но можно и указать вручную:
type t = int -> t 

let print_sum (x : int) : t =
  let rec f (acc : int) : t = fun (y : int) : t ->
    printf "acc(%d) + %d\n" acc y;
    f (acc + y)
  in 
  f x
  
let () = ignore (print_sum 10 20 30 40)

Языки которые смогли реализовать тест на лямбдах/функциях, их система типов и ее записи позволяет строить рекурсивные по возврату лямбды и функции:

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

Языки у которых пока не получилось без дополнительных средств типа классов/структур для обхода проблем с типами:

  • Rust (использование trait)
  • C (некорректная реализация)
  • Zig (использование классов)
  • D (использование делегатов)

Не являсь полностью статически типизированным языком, через свою систему типов смогли выразить пример

★★★★★

Последнее исправление: MOPKOBKA (всего исправлений: 21)
Ответ на: комментарий от anonymous

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

два вопроса :

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

  • и зачем рекурсивные типы для компиляторов? чему они помогут?

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Сразу как ответишь, причём, реально ответишь, на: чем так СУЩЕСТВЕННО отличается struct S(Box<dyn FnOnce(i32) -> S>); от type TAdder = reference to function(i: Integer): TAdder;- тип в рачте недостаточно новый, недостотаточно тип или что? Напишешь в плюсах… нет не бороучекер целиком это неадекватное требование, хотя бы минимальный вектор с итераторами при попытке слома которых(при изменении вектора) будет гарантированная ошибка компиляции вместо УБ, это вот будет пополезнее темы топика. Или вообще итераторы сравнимые по эффективности и удобству с растовыми, без заворачивания каждой функции в лямбду, это, кстати, прямо касается тонкостей систем типов. Или сымитировать полноценно трейты, не трейт объекты если чо

zurg
()
Ответ на: комментарий от alysnix

сори что врываюсь

боксинг это тегирование «raw-данных» буквальным маркером типа (в частности для дин резолвинга)

ибо для статик типов компилятору нет необходимости маркер что за данные тащить в бинарь целевого кода ибо семантика размазанна в операциях

qulinxao3 ★☆
()
Последнее исправление: qulinxao3 (всего исправлений: 1)
Ответ на: комментарий от alysnix

ocaml по слухам у реальных не вонабияйцеголовых а у практикующих яйцеголовых один из языков предварительной реализации для благодатности и безбаговости ибо таков путь

qulinxao3 ★☆
()
Последнее исправление: qulinxao3 (всего исправлений: 1)
Ответ на: комментарий от zurg

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

хотя бы минимальный вектор с итераторами при попытке слома которых(при изменении вектора)

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

Каждая строчка в расте это УБ. По определению. Не используй понятия значения которых не знаешь.

Или вообще итераторы сравнимые по эффективности и удобству с растовыми

Никаких раст-истораторов нет это обычная next-лапша из любой жавы. Никакой эффективности нет они сливают везде и всюду. Единственный шанс это обмазать всё форсинлайном в надежде что сишный компилятор оптимизирует там что-то. Но это не работает.

Самый такой просто пример - это невозможность приёма/передачи итераторов. Любой может взять эту лапшу и увидеть там collect. Это что-то на уровне жс где постоянно нужно в массив конвертировать итераторы чтобы с ними работать.

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

это, кстати, прямо касается тонкостей систем типов.

Ты даже что такое типы не знаешь. Система типов раста самое донное днище. Допустим банально это днище не умеет в классы итераторов. Комуинтересно посмотрит на size_hint-позорище и прочее подобное.

Или сымитировать полноценно трейты, не трейт объекты если чо

Трейты это огрызок перегрузки по одному параметру. Что ты там собрался эмулировать.

Вот смотри. Я выше как пример показывал что работает в цпп-коде который есть по ссылке у автора. Ты что-то написал? Ты что-то показать? Ну кроме позорной копипасты с нейронки без понимания того что она нагенерила?

Показывай все эти типы, трейты. Чего там тебе аналог нужен. Почему никто из нейроботов ничего не показывает никогда?

anonymous
()
Ответ на: комментарий от qulinxao3

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

на самом деле это не от хорошей жизни, и до такого рекомендуют не доводить.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

для продакшн после ocaml- реализации 2-3 гуру компилистроения передадун эталон в отдел реализации где 2-3 тоны китаяиндусов свояют на плюсах

зы

после взрыва ракеты погиб 1 тайконавт и 20 тон кочегаров

в ираке погибло 100 американцев и 40 тон местных полиармейцев

qulinxao3 ★☆
()
Ответ на: комментарий от alysnix

Вот видишь, а мы частенько от тебя похожее по стилю видим, ну поделив на сто, естественно. Как минимум, умеешь слова в предложения адекватно складывать, в отличие от этого царебота, и понималка в голове у тебя явно есть, только почему-то к расту(и не только) её не применяешь.

zurg
()
Последнее исправление: zurg (всего исправлений: 1)
Ответ на: комментарий от alysnix

ваще факт того что программист(пользователь) вынужден разбираться в сортах боксинга-анбоксинга проявление сырости реализации где компилятор(жабы а дальше nih c#) оказался не достаточно крут что бы самостоятельно погружать объекты(для проггера программиста всё есть объект в этом случае) в value типы

когда возможно с сохранением семантики программы

крч боксинг-анбоксинг это пример привнесённой сложности не иманентной прикладной области а обусловенное «приметивностью» языка ( в частности java и прочие oop-hype)

qulinxao3 ★☆
()
Последнее исправление: qulinxao3 (всего исправлений: 1)
Ответ на: комментарий от alysnix

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

Какое-то ооп/объекты и прочая чушь. Ничего этого нет. Сонительно что ты способен что-то понять, но авось другие поймут.

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

Даже если мы каким-то чудом это 5 из int[5] засунем внутрь функции, то это не сработает для ситуации когда там будет не пять. И для ситуации когда нам нужно будет сделать полиморфную функцию.

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

Аналогично гц не может(как и аллокатор в каком-нибудь донном всевдоси) не может работать с массивами/прочем. Он работает с ptr+len. Ему нужен единый интерфейс для взаимодействия со всеми объектами.

Аналогично когда нужно скопировать объекты. Как их копировать? Вот именно что универсальной копией что и делает жава/иная скриптуха.

Числовая константа и какая-то иная чушь - это копипаста из википедии. Ты опять не понял что там написано. Числовая константа там не потому что ооп, а потому что общий интерфейс.

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

anonymous
()
Ответ на: комментарий от alysnix

охлёль

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

но тогда выгоды от raw(уже не таких)-примитивных типов было бы меньше

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

qulinxao3 ★☆
()
Ответ на: комментарий от anonymous

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

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

alysnix ★★★
()
Ответ на: комментарий от zurg

только почему-то к расту(и не только) её не применяешь.

я оопешник по убеждениям. я думаю, что выход не в расте, а в post-c++, когда ему поправят грамматику, введут модули, и все такое.

Таки раст ооп скорее симулирует, и это ему еще слезками отплачется.

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

а пока раст лишь на стадии подьема на хайпе.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от anonymous

Вы подняли интересную тему - маркировка персонажей на форуме которые выдают AI slop за свое оч ценное мнение. Пока что единственный вариант - добавлять в игнор.

Obezyan
()
Последнее исправление: Obezyan (всего исправлений: 1)
Ответ на: комментарий от anonymous

А теперь напиши это на Rust.

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

Там выше @monk это сделал без тайпклассов. Всё норм.

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

hateyoufeel ★★★★★
()
Ответ на: комментарий от alysnix

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

Ты не поверишь, но… Первые версии компилятора Rust были написаны именно что на OCaml. А так, хватает: Coq, Compcert (там Coq и OCaml), ReasonML и ещё несколько.

Встречный вопрос: сколько современных компиляторов Си, умеющих хотя бы С23, написаны на Си?

hateyoufeel ★★★★★
()
Ответ на: комментарий от alysnix

Первые версии компилятора Rust были написаны именно что на OCaml.

на hh.ru есть одна вакансия на ocaml. 4 на лисп. и 1338 на с++. вот думаю, какой язык лучше.

Для того, чтобы работать на галере, лучше возьми Java или golang. Другой вопрос, что работу с OCaml найти не особо сложно. Просто ты не там ищешь.

hateyoufeel ★★★★★
()
Ответ на: комментарий от anonymous

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

Для отыгрыша. То есть для других игроков. И симулировать смерть от ран можно много раз, а реально помереть — только один.

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

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от anonymous

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

И, возвращаясь к Rust: есть те, кому нравится программирование в стиле ООП. Но в случае Rust они осознанно ограничивают это программирование симуляцией, а не полным воспроизведением.

monk ★★★★★
()
Ответ на: комментарий от monk

Есть люди, которым нравится жить как викинги (воспроизводить ремёсла тех времён, сражаться рукопашным оружием).

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

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

В общем, можно сказать проще. Одно дело реальный викинг, и совсем другое - викинг диванный/симулянт.

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

Нет, выбора у них нет.

И, возвращаясь к Rust: есть те, кому нравится программирование в стиле ООП. Но в случае Rust они осознанно ограничивают это программирование симуляцией, а не полным воспроизведением.

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

anonymous
()
Ответ на: комментарий от Nervous

Ну ты просто жп и вж. В целом, здесь два момента:

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

  • Заявляется ущербность ооп, но оно всё равно используется(притом даже не ооп, а наколенная симуляция от неосиливших ооп). Почему тезис вида «ну я симулирую те пару процентов ооп, которые действительно полезны» не работает, я объяснил выше. Чем являются подобные внутренние противоречия? - шизой или рабством. Ни в том, ни в другом случае о какой-то объективности можно даже не мечтать.

Кстати,

скорее всего будет ранняя седина и язва желудка

скорее всего

В предыдущем посте было «точно»/«инфа сотка», а тут уже «скорее всего». Далее там будет что-то навроде «лично для меня»/«по моему скромному мнению».

anonymous
()
Ответ на: комментарий от MOPKOBKA

type T func(int) T … Там тоже динамическая типизация?

Я считаю, что это тоже самое, что в C# Func<int, dynamic> (здесь dynamic соответствует твоему возвращаемому T). То есть у нас функция, которая принимает int, а возвращает что-то абстрактное, меньше подверженное проверкам. При том, в C# в это абстрактное нельзя засунуть что попало, например, такой код не скомпилируется:

int f2(int t)
{
    return 0;
}

Func<int, dynamic> f1(int t)
{
    return f2;
}

Когда мы в C используем void* то мы говорим компилятору - я буду пихать по этому адресу что попало, а ты не проверяй. Так же и с dynamic. В чём разница? Или ты думаешь, что C# пихает в каждую переменную с типом dynamic отдельный питоновский интерпретатор?

уверяя что в статической типизации невозможно выражать рекурсивные типы…

Тут надо уточнить. Структура может содержать указатель или ссылку на другую структуру такого же типа. Структура не может содержать сама себя.

Kogrom
()
Ответ на: комментарий от Kogrom

Структура может содержать указатель или ссылку на другую структуру такого же типа.

Там тоже dynamic? Или чем такая рекурсия отличается от возврата функции?

И еще вопрос, это тоже dynamic?

f:
  mov rax, f 
  ret

g:
  cmp rdi, 0
  jg .g_lt_0
  mov rax, f
  ret
.g_gt_0:
  mov rdi, 10
  jmp f

main:
  call f
  jmp rax
  
  mov rdi, 0
  call g
  jmp rax

  mov rdi, 100
  call g
  jmp rax

MOPKOBKA ★★★★★
() автор топика
Последнее исправление: MOPKOBKA (всего исправлений: 3)
Ответ на: комментарий от anonymous

Заявляется ущербность ооп, но оно всё равно используется

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

В итоге им вполне можно стало пользоваться как способом организации вычислительного процесса, не вырывая себе все волосы на жопе (особенно при поддержке и доработке). А в сочетании с неизменяемыми данными — очень даже можно пользоваться %)

Более широкое/мощное/полноценное решение всегда можно свести к менее широкому/мощному/полноценному

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

ты просто жп и вж

Будьте добры, переведите с зумерского.

Nervous ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Там тоже dynamic?

Нет. Там же указатель на конкретный тип структуры. Это не dynamic. Отличается тем, как компилятор проверяет. Кроме того, от указателя на void конкретный указатель отличается тем, как принимающий код будет использовать результат.

И еще вопрос, это тоже dynamic?

Вопрос некорректный. У этого ассемблера нет типов, поэтому здесь не делится на статику и динамику. Он просто оперирует ячейками в памяти, адресами, которые в этих ячейках лежат. А f, g и main это просто метки, а не функции. Ты к ним можешь перейти как через колл, так и через какой-нибудь джамп. Разница будет только через какую команду ты к ним перешёл.

Kogrom
()
Ответ на: комментарий от Kogrom

Нет. Там же указатель на конкретный тип структуры.

А в Go на конкретный тип функции.

Отличается тем, как компилятор проверяет.

В чем отличие от Go?

Вопрос некорректный.

Корректный, dynamic/variant как объект может быть в любой системе.

MOPKOBKA ★★★★★
() автор топика
Ответ на: комментарий от alysnix

А в классическом ООП уже решили давнюю задачу с фигурами?

Вот есть треугольник, круг, прямоугольник. Где должен быть метод проверяющий их столкновения, у них самих, или во внешнем классе? А если для некоторых из них реализованы оптимизированные функции (fastCheckCollisionTriangleCirlce[Triangle, Circle] к примеру), то как их вызывать, ведь фигуры могут быть переданы как [Circle, Triangle], или [Triangle, Circle], делать swap()?

MOPKOBKA ★★★★★
() автор топика
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

А в Go на конкретный тип функции.

Какой же он конкретный, если он T? Конкретный - это когда int, double, конкретные указатели на них и т.д. А T - непонятно что.

В чем отличие от Go?

Если типы конкретные, то компилятор заругается, если вставишь непонятно что. А если абстрактные - то пропустит.

Корректный

Скажи тогда, ассемблер - это язык со статической типизацией или с динамической?

variant

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

Kogrom
()
Ответ на: комментарий от Kogrom

Какой же он конкретный, если он T?

Именование типа делает его неконкретным? Ладно, мне кажется ты издеваешься.

type T = int

И уже неконкретный тип, удивительно!

int abc;

Конкретный

#define T int

T abc

неконкретный

MOPKOBKA ★★★★★
() автор топика
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA
  1. растишка не дает ничего нового, и на с++ можно писать аналогично грустно и многословно, как и на растишке. потому что растишкины приколы - есть подмножество плюсовых абстракций. просто не наследуй, и дубась все на интерфейсах. медленно и печально.

так что любой вопрос про якобы недостатки с++ обрушиваются на растишку с еще большей силой.

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

разумеется если «физ тело фигура» собралось само реализовать просчет столкновения с другими, нужен базовый класс <физ-тело>, что будет хранить и реализовывать все общие свойства физ тел. возможно именно он и должен реализовывать общий функционал расчета столкновения двух тел на основе их конкретных особенностей. тогда там вообще пофиг, кто из них кто.

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

alysnix ★★★
()
Ответ на: комментарий от MOPKOBKA

…продолжение банкета

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

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

alysnix ★★★
()