Ну потому что из-за того что тип аргумента разный функция дальше будет по-разному компилироваться и соотвественно без дублирования тела функции не обойтись
Инстанцирование делает то же самое, в общем-то.
Это да, но вот хотелось бы иметь такое же инстанциирование и для return-пути. Сейчас в С++ запрещается возвращать из функции (по рантайм-условию) разные типы (которые никак не связаны между собой) auto func(){ if(rand() % 2){ return A{}; } else { return B{} } } struct A { int field; } struct B { double field; int field2; } а если компилятор будет делать под капотом вот такое преобразование тела функции auto res1 = func(); auto res2 = func(); auto result = process(res1, res2); в имплисит-лямбда-коллбек func([](auto res1){ func([](auto res2){ auto result = process(res1, res2); }); }); ну а в самой функции return будет трансформирован в вызов лямбда-функции auto func(auto fn){ if(rand() % 2){ fn(A{}); } else { fn(B{}); } } то этот лямбда-коллбек дальше будет проинстанциирован отдельно для структуры A и структуры B (которая никак не связана с A) И в результате код продолжит работать с конретным типом и мы реализовали в языке поддержку (как я понимаю довольно уникальной киллер-фичи) возможности возвращать из функции разные типы/классы/структуры по рантайм-условию
не совсем, тип возрата должен не зависеть от параметра а возвращаться из функции по рантайм условию например struct A {...}; struct B {...}; auto foo(){ if(rand() % 2){ return A(); } else { return B(); } } то есть если в результате вызова функции rand() было получено случаное число которое делится на два то функция foo() возвращает структуру A а иначе возвращает структуру B
Так, падажжи. Как это тогда в сигнатуре функции-то записать? data Either e a = Left { left :: e } | Right { right :: a } foo :: Int -> Either String Int foo x = if odd x then Right x else Left "not odd" Так?
я не знаю как записать в сигнатуре, мне кажется весь смысл вывода типов в том чтобы не нужно было писать сигнатуру а тип возврата функции выводился автоматически. И суть этой киллер-фичи которую я пытаюсь объяснить заключается в том что если функция возвращает разные типы по рантайм условию то они не будут боксится в различные полиморфные обертки, вместо этого весь последующий код будет работать с конкретным статическим типом
В итоге после трёх вызовов (один из которых мы сделали по ошибке) выражение типа превращается в кашу. Я считаю, что нет, выводить типы топлевел-функций вообще нельзя, у них обязаны быть сигнатуры.
И кроме того различить ситуацию со структурами A и B после rand() по возврату из функции будет невозможно.
Возможно, в этом и суть этой фичи которая похожа на "продолжения" или CPS-like трансформации. Компилятор просто возмет код который работает с результатом функции auto main(){ auto result = func(); process(result); } и переделает его в передачу имплисит лямбда-коллбека и в теле самой функции вместо return будет вызван этот лямбда-коллбек auto main(){ func([](auto result){ process(result); }); } auto func(auto fn){ if(rand() % 2){ fn(A{}); } else { fn(B{}) } } Ну а дальше компилятор будет делать так называемое инстанциирование этой лямбды - то есть для каждого типа аргумента продублирует тело функции и для каждой копии функции будет выведен свой тип аргумента auto main(){ func([](auto result){ process(result); }); } auto func(auto fn){ if(rand() % 2){ fn(A{}); //process(A{}) } else { fn(B{}) //process(B{}) } } И таким образом несмотря на то что мы возвращаем из функции разные типы по рантайм условию - для каждой возможной ветки выполнения последующий код программы будет работать со своим конкретным типом без боксинга в полиморфную обертку
Мы вызываем в какой-то функции func 5 раз, у нас 32 копии объемлющей функции. А, ещё одна проблема — код, следующий за вызовом func, как он будет работать с разнородными результатами func?
я ниасилил
a = func() код, работающий с `a` - как один и тот же код может обработать `int` и `struct B`, например?
Надо полагать, он и дальше будет заворачиваться, пока требующих того расхождений не останется.
ну если у них общий интерфейс (например у struct B есть перегружены математические операторы) - то вполне может. Ну а если нет то тогда будет ошибка компиляции. То есть последующий код должен работать с результатом как будто с пересечением типов но сам тип он будет статически выводиться без полимфорного боксинга
Кажется Вы здесь смешиваете систему типов и рантайм семантику. Кажется если использовать обычные A | B, то семантически получится тот же самое. За исключением перфа, конечно, но он не имеет отношения к типизации. Вы можете привести пример, который возможен под Вашим предложением, но не будет работать с юнионом(с очевидными расширениями для их поддержки)?
Ну если отбросить аспект производительности и представить что под капотом все работает через структурную полимофность (как динамические языки) то в принципе можно представить эту фичу как то что результат возврата функции (которая возвращает разные типы по рантайм-условию) будет выводиться как пересечение A | B. Но все же в статически-типизированном языке хочется иметь именно статические типы а не полиморфные обертки которые сильно снижают производительность. И вот среди таких статически-типизированных языков я еще не разу не встречал эту фичу когда можно возвращать разные типы по рантайм-условию и дальше можно работать с результатом и мы будем статически обращаться к полям полученного объекта максимально быстро (чтение по конкретному смещению)
Обсуждают сегодня