1. Java / Говнокод #26515

    0

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    public class Main {
      public static void uh() {
        try {
          
        } catch (Exception e) {
          throw e;
        }
      }
    
      // <no errors>
    
      public static void oh() {
        try {
          throw new RuntimeException();
        } catch (Exception e) {
          throw e;
        }
      }
    
      // <no errors>
    
      public static void snap() {
        try {
          throw new Exception();
        } catch (Exception e) {
          throw e;
        }
      }
    
      // /tmp/Main.java:8: error: unreported exception Exception; must be caught or declared to be thrown
      //       throw e;
      //       ^
      // 1 error
    }

    Где-то посередке между хорошим inference и остутствием интуитивности

    Запостил: Fike, 20 Марта 2020

    Комментарии (143) RSS

    • > остутствием

      хоть бы раз без ошибок пост оформил, пидор ёбаный
      Ответить
    • Кстати в яве запросто можно кидаться checked исключениями без throws.
      Ответить
      • ну, не совсем запросто, но да, паттерн окончательно сломан
        Ответить
        • Ну там оно один раз заворачивается в простенький метод, с генериком и одним ворнингом.
          А дальше уже этот метод (без throws) используется по коду.
          Ответить
    • Какой C# )))
      Я кста взглянув на код, подумал что это C#.
      Вот что в нашем C# хорошо - так это то, что нам не нада писать implements, extends, throws.
      Хотя я когда выполнял лабораторки в унике по Java, в throws писал throws Throwable() - а че, мне ваша ява нафиг не сдалась, но нада тупа сдать.
      Ответить
      • показать все, что скрытоvanished
        Ответить
        • throws - что-то подобное в Goвне (или Rust?) присутствует. Тип хня, которая требует проверить на ошибку или передать ее дальше по инстанциям.
          Ответить
          • В го, но если в джаве метод A бросает IOException может быть завернут без дополнительных бубнов в B, у которого в сигнатуре тоже выброс IOException, то в го тебе блядь везде надо ставить ифы, чтобы проверять, не вернул ли дочерний метод ошибку.

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

            https://awalterschulze.github.io/blog/monads-for-goprogrammers/bartiferr.png
            Ответить
            • Так они же специально язык без исключений сделали.
              Go старые сишники пилили.
              И всё бы ничего, да только из-за gc вся его сишность насмарку.
              Ответить
              • Они типа делали безопасный новый C (с преферансом и женщинами легкого поведения), но вот вышло из этого говно какое-то, а не нормальный язык.
                Ответить
                • Именно поэтому я за «PHP».
                  Ответить
                  • Именно поэтому я за C++, который сохранил принцип zero-cost abstaction.
                    Ответить
                • показать все, что скрытоvanished
                  Ответить
                  • > в результате чего вся программа это один файл, который можно скопировать, и он будет 20 лет работать, пока userland abi не поменяют.

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

                    > Так же слышал, что там нет обобщенного программирования. Это правда? Как же они делают контейнеры объектов?

                    Переписывая один и тот же код под разные типы. И это норма.
                    Ответить
                    • Не соберет. И работать если будет, то только за счет хлама, тщательно накопленнового в %WINDIR%\winsxs\
                      Ответить
                      • > не соберет

                        ну привет

                        https://docs.microsoft.com/en-us/dotnet/core/deploying/deploy-with-cli#self-contained-deployment
                        https://github.com/dotnet/core/blob/master/Documentation/prereqs.md

                        тут вообще недавно постили статью, где чувак шарповую прогу ужал до восьмикилобайтного экзешника
                        Ответить
                        • показать все, что скрытоvanished
                          Ответить
                        • Помню, игрунок еще не нашел ничего лучше, как написать об этом не куда-то там, а в medium. А все потому, что икосистеме Visual BRASIC принято не замечать рантайм.

                          Впрочем, мокрософт заботливо доставит этот рантайм через automatic updates и даже перезагрузит пека для тебя.
                          > all required .NET Core files to run your app but it doesn't include the native dependencies of .NET Core. These dependencies must be present on the system before the app runs.
                          Ответить
                          • Впрочем, мокрософт заботливо доставит этот рантайм через automatic updates и даже перезагрузит пека для тебя лишь бы ты на него не смотрел.
                            Ответить
                          • для тебя ж prereqs приложил, ты сходи по второй ссылке что ли. как будто у тебя го без libssl или libicu сможет работать с сертификатами и юникодом.
                            Ответить
                            • > го без libssl или libicu сможет работать
                              Такой-то перевод стрелок. Как будто .NOT сможет работать со строкой "Hello world!" без кучи предустановленного барахла, но которое к счастью поставляется из коробки вместе с windows analytics.
                              Ответить
                    • показать все, что скрытоvanished
                      Ответить
                  • > и что он всё собирает статически, в результате чего вся программа это один файл

                    Обычно занимающий сотни мегабайт.
                    Ответить
        • Ну в джаву optional к счастью еще в восьмерке завезли, а шарп пока подтупливает. В нугете всё есть, конечно, но с говнецой: там Настоящие Функциональщики не делают конструктора OfNullable, потому что конечный метод должен знать, возвращает он пустоту или значение. В теории это конечно правильно, но как мне блядь стыковать это с легаси кодом и библиотеками, которые духом не слыхивали про этот тип из нестандартной библиотеки? Сначала просто мучаешься с x == null ? None : Some(x), потом плюешь и запихиваешь это в свой статический конструктор, потому открываешь следующий проект и там сука опять делаешь то же самое.

          Извините, накипело.
          Ответить
          • x == null ? None : Some(x) - это уже легаси
            x?.Some() ?? None
            Ответить
          • показать все, что скрытоvanished
            Ответить
            • >Optional говно
              Подтверждаю.
              Причём бойлерплейтное.
              Монада есть, а трансформеры не завезли.
              Ответить
              • показать все, что скрытоvanished
                Ответить
                • Это Result, а не Optional
                  Ответить
                  • А «Optional» — замечательный образец анскильного говна. Им крайне удобно сделать быстро и анскильно, а потом долго и нудно разгребать получившееся говно в попытках избавиться от «Упс, что-то пошло не так».
                    Ответить
                    • Странно, это какие-то джавапроблемы. У нас в Свифте всё нормально
                      Ответить
                      • Это проблемы не языка, это проблемы концепта. В большинстве случаев «Optional» — ущербное говно, удобное здесь и сейчас, но создающее серьёзные проблемы в дальней перспективе.

                        Разумеется, в некоторых случаях «Optional» может быть полезным: например, какому-нибудь методу поиска элемента в коллекции действительно удобнее возвращать «Optional». Но попытки использовать его в более сложных случаях (что-нибудь вроде «db.getUser(username, password) -> Optional<User>») приводят именно к тому, о чём я и сказал: к невразумительным ошибкам и большим сложностям в рефакторинге.
                        Ответить
                        • Я считаю что Optional» — ущербное говно, малоудобное даже здесь и сейчас.
                          Питушарская, тупая обёртка для анскилябр.

                          >например, какому-нибудь методу поиска элемента в коллекции действительно удобнее возвращать «Optional»

                          indexOf до сих пор возвращает -1. И никто не жалуется. И не городит вместо int сложных типов c позицией и флагом ошибки.
                          Тем более что абстракция «Optional» пока что не zero-cost.
                          Ответить
                          • > indexOf до сих пор возвращает -1. И никто не жалуется.
                            Error-prone питушня.

                            Optional с компайл-тайп проверками гораздо надёжнее. Что-то вроде такого псевдокода:
                            [Image] image1 = images.findByName('petooh');  // Ошибка: findByName() возвращает Optional
                            [Optional<Image>] image2 = images.findByName('petooh');  // OK
                            printer.print(image2);  // Ошибка: print() принимает Image, не Optional
                            printer.print(image2.getValue());  // Ошибка: image2 не проверено на NULL
                            if image2.hasValue()
                                printer.print(image2.getValue());  // ОК


                            Разумеется, это только набросок, чтобы дать общее представление о функционале.
                            Ответить
                            • Посоны говорили, что они включают на уровне линтера проверки, что на Optinal не вызывается метод get.
                              if image2.hasValue()
                                  printer.print(image2.getValue());  // ОК

                              Не-не-не-не. Вы что??? If это параша, для императивных старпёров.

                              Уже лет 5 как западло писать if, for, while.

                              Сейчас модно писать функциональненько
                              image2.ifPresent(i->printer.print(i))
                              https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#ifPresent-java.util.function.Consumer-

                              >Optional с компайл-тайп проверками гораздо надёжнее.
                              Возможно. Только лямбды и обёртки снижают скорость кода. А пользы особой не дают.
                              Ответить
                              • > Только лямбды и обёртки снижают скорость кода.
                                Лямбды — возможно. Обёртки в крестовом зерокост стиле — настолько несущественно и в настолько экзотических случаях, что этим можно пренебречь, поскольку выбор небезопасных интов только из-за скорости в 99.95% случаях будет классической и вредной premature optimization.

                                > А пользы особой не дают.
                                Они дают гарантию того, что полученный -1 не будет где-нибудь ниже по коду засунут в индекс. В анскильной питушне с bounds-checking это будет неожиданное исключение, а вот в крестосях — UB, что гораздо печальнее.
                                Ответить
                                • >Обёртки в крестовом зерокост стиле — настолько несущественно и в настолько экзотических случаях, что этим можно пренебречь, поскольку выбор небезопасных интов только из-за скорости в 99.95% случаях будет классической и вредной premature optimization.

                                  Ну я же за Царcкий режим топлю.
                                  В общем я считаю Optional частным видом списка из 0 или 1 элемента.
                                  А сам список можно представить в виде массива.

                                  >Они дают гарантию того, что полученный -1 не будет где-нибудь ниже по коду засунут в индекс.

                                  Ну не проверять код возврата indexOf, это уж совсем для анскильных лохов.
                                  Ответить
                                  • > В общем я считаю Optional частным видом списка из 0 или 1 элемента.
                                    Он и есть.

                                    > Ну не проверять код возврата indexOf, это уж совсем для анскильных лохов.
                                    Любые программы по определению пишутся невнимательными анскильными лохами (степень анскильности, конечно, варьируется). Одна из главных задач языка программирования высокого уровня — снизить количество ошибок, вызванных анскильностью и невнимательностью. В этом смысле Optional с жёсткими компайл-тайм проверками гораздо лучше простого инта.
                                    Ответить
                                    • Я не спорю. Но пока не видно приличного zero-cost, ложащегося в регистр
                                      struct Index{
                                          uint pos:31
                                         ,int error:1
                                      }


                                      >Они дают гарантию того, что полученный -1 не будет где-нибудь ниже по коду засунут в индекс.

                                      Это редкая ошибка.

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

                                      И её хер обойдёшь возвращая Optionalы.

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

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

                                      indexOf по строке должен возвращать
                                      строго тип int[0,length-searchString.length-1].
                                      А substrы должны принимать строго тип int[0,length-1]

                                      И компилятор при инкременте int[0,length-1] должен возвращать тип int[1,length]. А при добавлении 42 тип будет int[42,length-41]

                                      Соответственно при желании полезть в строку/массив с таким типом программа просто не соберётся.
                                      Ответить
                                      • > Но пока не видно приличного zero-cost, ложащегося в регистр
                                        Да. Нужен полностью компайл-тайм Optional, а не всякая анскиллушня.

                                        > И её хер обойдёшь возвращая Optionalы.
                                        Очень даже обойдёшь. Нет индексов — нет ошибок с приплюсовыванием.

                                        > indexOf по строке должен возвращать строго тип int[0,length-searchString.length-1].
                                        А как быть со строками, которые вводит пользователь?
                                        Ответить
                                        • >А как быть со строками, которые вводит пользователь?

                                          А компилятор может считать длину строки за некий N. И типы делать [0,N-1]

                                          var i=s.indexOf("koko") //[0,N-1-4]
                                          //к i можно прибавить число не больше 4, чтобы не вылезти за пределы.

                                          Как только вылазим за тип — выдавать ошибку.

                                          >Да. Нужен полностью компайл-тайм Optional, а не всякая анскиллушня.

                                          Да. См. ниже. Паттерн-матчинг.
                                          Ответить
                                          • Тогда в игру вступает проблема останова, и в результате в половине случаев компилятор всё выведет сам, а в другой половине (например, индекс возвращается из другой функции, или из глубины вызовов, или из прошедшего проверку пользовательского ввода, etc) программисту придётся ебаться с явными проверками. При этом правила, когда компилятор может автоматически проверить индекс, а когда не может, будут настолько сложны и запутаны, что программисту придётся компилировать программу только чтобы понять, работает она или нет — или просто проверять индексы везде, что перфоманса не добавит.

                                            > Да. См. ниже. Паттерн-матчинг.
                                            Для самых простых вещей, вроде collection.find(), это оверинжиниринг.
                                            Ответить
                                            • > а в другой половине (например, индекс возвращается из другой функции,

                                              Значит нужно допиливать автовывод типов.

                                              >или из глубины вызовов

                                              С индексной арифметикой, да ещё разбросанной по глубине вызовов можно страшно налажать. Уж лучше иметь ошибку компиляции, чем хрупкий код-пиздец.
                                              В одном месте поменяли, в другом забыли. Another buffer overflow CVE.

                                              >или из прошедшего проверку пользовательского ввода
                                              Проверку он пройдёт только конверсией в нужный тип.
                                              В принципе по объёму кода это одно и то же.

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

                                              Ничего сложного там нет. Просто buffer overflow говно больше никогда не скомпилится и не попадёт в продакшн.

                                              >просто проверять индексы везде, что перфоманса не добавит.

                                              В случае анскильного отброса с руками из жопы — безусловно.
                                              Положительным моментом будет опять-таки то что buffer overflow говно больше никогда не скомпилится и не попадёт в продакшн.
                                              Ответить
                                              • > Значит нужно допиливать автовывод типов.
                                                Полностью это сделать невозможно.

                                                > buffer overflow
                                                Про эти страшные слова забыли в любых языках с проверками границ.

                                                > Проверку он пройдёт только конверсией в нужный тип.
                                                Как именно? Как нужно будет переписать, например, такой код:
                                                string = getUserInput();
                                                print(string[3]);

                                                ?

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

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

                                                > buffer overflow говно больше никогда не скомпилится и не попадёт в продакшн
                                                Для этого достаточно просто избавиться от индексов, как от тяжёлого наследия ассемблера.
                                                Ответить
                                                • >string = getUserInput();
                                                  >print(string[3]); //здесь будет runtime error

                                                  Во-первых этот код — опасное говно. И хорошо, что он не скомпилится.

                                                  String[0..N] str = getUserInput();
                                                  int[0..N] index=cast(3); //здесь будет runtime error
                                                  print( index );


                                                  >когда компилятор не сможет правильно доказать корректность индекса

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

                                                  >Про эти страшные слова забыли в любых языках с проверками границ

                                                  И расплатились 2-4 кратным снижением пирфоманса.
                                                  Ответить
                                                  • > Во-первых этот код — опасное говно. И хорошо, что он не скомпилится.
                                                    Хорошо, давай так:
                                                    def capitalize(string):
                                                        string[0] = to_upper(string[0])
                                                    ...
                                                    name = getUserInput()
                                                    capitalize(name)

                                                    Это достаточно реальный пример. Как его переписать так, чтобы
                                                    >>> buffer overflow говно больше никогда не скомпилится и не попадёт в продакшн
                                                    ?

                                                    > И расплатились 2-4 кратным снижением пирфоманса.
                                                    Даже в теории, даже для самого плохого случая вида «for i in 0..len(arr): arr[i] += 1» никаких x2 и в помине не будет.
                                                    Ответить
                                                    • >никаких x2 и в помине не будет.

                                                      Где-то на ГК неоднократно публиковались эти цифры.
                                                      Там в сишке включали какой-то режим bounds checking и емнип пирфоманс садился в 2-4 раза.

                                                      Я сам охуел когда увидел.

                                                      >def capitalize(string):
                                                      В этом коде в любом случае придётся написать if string.len>0
                                                      typedef NotEmpty Array[N] where <N is int[1..MAX_SIZE]>
                                                      
                                                      def capitalize(NotEmpty str):
                                                          string[0] = to_upper(string[0])

                                                      Теперь наш capitalize не принимает строки нулевой длины.
                                                      И проблема перезжает в вызов:
                                                      NotEmpty name = cast(getUserInput()); //здесь оно отвалится
                                                      capitalize(name)
                                                      Ответить
                                                      • > Там в сишке включали какой-то режим bounds checking и емнип пирфоманс садился в 2-4 раза.
                                                        Очевидно, это был простой наброс на хреновый bounds checking. С нормальным чекером и нормальным компилятором, например, такого точно не будет.
                                                        Реальный пример: https://gcc.godbolt.org/z/DdP-Qj. «Gcc» достаточно умён, чтобы доказать нинужность лишних проверок границ. В данных случаях получился идеальный зерокост.

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

                                                        > //здесь оно отвалится
                                                        Нужно сделать так, чтобы не отвалилось.
                                                        Ответить
                                                        • >Нужно сделать так, чтобы не отвалилось.
                                                          Это никак невозможно, если придёт пустая строка.

                                                          > string[0] = to_upper(string[0])
                                                          Либо отвалится здесь, с out of bounds.

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

                                                          Явно сконвертировать этот индекс в тип [0..N].
                                                          Если конверсия будет неудачной — произойдёт runtime ошибка.
                                                          Но т.к. программист уверен в годности индекса, в теории ошибок быть не должно.
                                                          Ответить
                                                          • > Это никак невозможно, если придёт пустая строка.
                                                            Я написал код с ошибкой (выхода за границы массива), которую предложил исправить.
                                                            >>> Я хочу понять, как программист должен будет говорить компилятору, что данный индекс проверен и точно не вылезет за границы рантайм-массива/строки.

                                                            Просто переведи на «систему типов с диапазонами»:
                                                            name = getUserInput()
                                                            if len(name) > 0:
                                                                name[0] = to_upper(name[0])
                                                            Ответить
                                                            • >>> Просто переведи на «систему типов с диапазонами»

                                                              name = getUserInput();
                                                              int[0..len(name)-1] indx=cast(0); 
                                                              name[indx] = to_upper(name[indx]);


                                                              В случае неявных преобразований типов вторую строчку можно убрать и мы автоматом получим язык с runtime bounds checking.
                                                              Ответить
                                                              • > int[0..len(name)-1]
                                                                Стоп, стоп, стоп. Это статический или динамический тип?

                                                                Ну и в любом случае, для правильного перевода не хватает ручной проверки на пустоту строки, поскольку приведённый мной в последнем комментарии отрывок на ней не упадёт.
                                                                Ответить
                                                                • >Стоп, стоп, стоп. Это статический или динамический тип?
                                                                  String[0..N] name = ...
                                                                  int[0..len(name)-1]  //компилятор видит что ф-ция len (string [0..N] )возвращает N+1
                                                                  int[0..N+1-1] //он делает простую подстановку и получает тип
                                                                  int[0..N] indx=cast(0);  //если мы примем гипотезу о тупости компилера, программист может писать N руками


                                                                  Такое можно накрестячить даже на современных шаблонах.

                                                                  >Ну и в любом случае, для правильного перевода не хватает ручной проверки на пустоту строки,
                                                                  Ага. Ниже пример.
                                                                  Ответить
                                                                  • Понятно.

                                                                    Тем не менее, я всё ещё не вижу никаких реальных преимуществ такой системы даже по сравнению с обычным неявным bounds checking. Абсолютное большинство индексаций в реальном коде происходит на рантайм-массивах либо же с рантайм-индексами. Доступ по компайл-тайм-вычислимому индексу к компайл-тайм-вычислимому массиву, который такая система может проверить в компайл-тайме, крайне редок.
                                                                    Всё остальное переходит в рантайм-проверки («cast()»).

                                                                    Более того, как я показывал выше (https://gcc.godbolt.org/z/DdP-Qj), мощные компиляторы вполне способны проводить индексные доказательства и выкидывать лишние проверки даже без диапазонных (хотя, конечно, в данном случае правильнее будет «предикатных») типов.
                                                                    Ответить
                                                                    • >Всё остальное переходит в рантайм-проверки («cast()»).

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

                                                                      В cast не переходят
                                                                      Srting x="kokoko";
                                                                      x[3];

                                                                      В cast не переходят штуки с indexOf/substring, завязанные на строку.

                                                                      >Абсолютное большинство индексаций в реальном коде происходит на рантайм-массивах либо же с рантайм-индексами.

                                                                      Поправочка. На рантайм-массивах либо же вместе с рантайм-индексами.
                                                                      А тип рантайм-индекса, у нас всегда завязан на рантайм-размер рантайм-массива.

                                                                      Обычная задача парсинга: найти начало подстроки, найти конец подстроки. Вырезать нужное.

                                                                      indexOfы будут возвращать тип [O..N] которым можно будет индексировать строку без всяких кастов.

                                                                      > мощные компиляторы вполне способны проводить индексные доказательства и выкидывать лишние проверки

                                                                      Даже ява с 7ой версии научилась.
                                                                      Но для примера сложнее тестового, компилер без подсказок в виде предлагаемой диапазонной типизации может обосраться с их выпиливанием.
                                                                      Ответить
                                                                      • > Нет. cast нужен только для абсолютных чисел (42) по рандомным строкам полученным извне.
                                                                        А также для индексов, полученных из рантайма.

                                                                        > Поправочка.
                                                                        Неверная поправка. Реальный пример:
                                                                        page_idx = getUserInputInt();
                                                                        page = pages[page_idx];
                                                                        print_page(page);

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

                                                                        > Обычная задача парсинга: найти начало подстроки, найти конец подстроки. Вырезать нужное.
                                                                        > indexOfы будут возвращать тип [O..N] которым можно будет индексировать строку без всяких кастов.
                                                                        И это прекрасно доказывается компилятором безо всяких бойлерплейтных [0..N] (ну, в теории: на практике, поскольку в «C++» строк нет, компилятор объёбывается).

                                                                        Факт в том, что вот эти вот «[0..N]» в контексте проверок индексов не несут никакой полезной информации. Компилятор сам прекрасно знает, что если переменная используется для индексации, то её значение должно лежать в пределах [0..N). Ничего нового программист компилятору не говорит.
                                                                        Более того, и не может сказать, потому что программист не может достоверно знать, что введёт пользователь. Ему, программисту, всё равно придётся явно писать «if idx < len(arr) ...» — и из этого компилятор прекрасно выводит все предикаты.
                                                                        Ответить
                                                                        • Добрый вечер. Можно продолжить.

                                                                          >Здесь в любом случае потребуются рантайм-проверки, либо через неявную проверку границ, либо через явный if.

                                                                          Да. И компилятор нам об этом сообщит что их НУЖНО написать.
                                                                          Таким образом, не дав собрать плохой код.

                                                                          >И это прекрасно доказывается компилятором безо всяких бойлерплейтных [0..N] (ну, в теории: на практике, поскольку в «C++» строк нет, компилятор объёбывается).

                                                                          Ок. Раз всё так прекрасно, это всё говно и бойлерплейт, непонятно почему по-прежнему десятки-сотни CVE по переполнению буфера?
                                                                          Ответить
                                                                          • > Да. И компилятор нам об этом сообщит что их НУЖНО написать.
                                                                            Компилятор об этом может сообщить и без [0..N]-бойлерплейта. Он и так это знает.

                                                                            > непонятно почему по-прежнему десятки-сотни CVE по переполнению буфера?
                                                                            Потому что «C».
                                                                            В «Java», «Python» и прочих анскильных языках переполнение буфера приведёт только к падению приложения — в самом худшем случае.

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

                                                                            Перейдём к реальным примерам. Возьмём произвольную достаточно объёмную опенсорсную прогу и проверим: https://github.com/microsoft/vcpkg. Я прошёлся по её коду грепом findstr'ом и обнаружил занимательную вещь: индексирования массивов/векторов там практически нет, в среднем на один файл встречается по паре-тройке примитивных индексирований (вида «return &data[0]»). [To be continued…]
                                                                            Ответить
                                                                            • >Компилятор об этом может сообщить и без [0..N]-бойлерплейта. Он и так это знает.

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

                                                                              Как уже сказано ранее:
                                                                              >>>Крестокомпилятор до сих пор и компайл-тайм деление на ноль отловить не всегда может, а ты замахиваешься на настолько грандиозные вещи.

                                                                              >В «Java», «Python» и прочих анскильных языках переполнение буфера приведёт только к падению приложения

                                                                              Есть две крайности:
                                                                              1. всовывать runtime-проверки на каждом обращение.
                                                                              2. не делать никаких проверок в принципе

                                                                              Того что предлагаю я: сообщать об ошибке и требовать ручной каст в правильный тип, только при условнии невозможности доказательства корректности кода (индексы типа 42 по массивам известным в runtime)

                                                                              То есть безопасные случаи без проверок будут работать как и раньше. А весь пользовательский ввод придётся валидировать.
                                                                              Ответить
                                                                              • > Если из глубины вызовов пришёл обычный int, компилятор не может точно сказать нужна проверка или нет.
                                                                                А так из глубины вызовов придёт int[0..N] с неопределённым диапазоном, привязанным к какому-то левому массиву.

                                                                                > сообщать об ошибке и требовать ручной каст в правильный тип, только при условнии невозможности доказательства корректности кода
                                                                                Я предлагаю абсолютно то же самое. Только без бойлерплейта в виде int[0..N], который никакой новой информации не несёт.

                                                                                > 1. всовывать runtime-проверки на каждом обращение.
                                                                                Я уже показал, что, например, «gcc» может доказать, что переменная никогда не выйдет за границы массива, и выкинуть ненужные проверки. Значит, для реализации надёжной индексации достаточно развить его систему доказательств и сделать ворнинг, когда для доказательства недостаточно информации.
                                                                                Ответить
                                                                          • Самое большое скопление говна индексов обнаружилось здесь: https://github.com/microsoft/vcpkg/blob/master/toolsrc/src/vcpkg/base/hash.cpp, и выглядит оно примерно так:
                                                                            std::uint32_t local[8];
                                                                            std::copy(begin(), end(), std::begin(local));
                                                                            
                                                                            for (std::size_t i = 0; i < number_of_rounds; ++i)
                                                                            {
                                                                                const auto a = local[0];
                                                                                const auto b = local[1];
                                                                                const auto c = local[2];
                                                                            
                                                                                const auto s0 = ror32(a, 2) ^ ror32(a, 13) ^ ror32(a, 22);
                                                                                const auto maj = (a & b) ^ (a & c) ^ (b & c);
                                                                                const auto tmp1 = s0 + maj;
                                                                            
                                                                                const auto e = local[4];
                                                                            
                                                                                const auto s1 = ror32(e, 6) ^ ror32(e, 11) ^ ror32(e, 25);
                                                                                const auto ch = (e & local[5]) ^ (~e & local[6]);
                                                                                const auto tmp2 = local[7] + s1 + ch + round_constants[i] + words[i];
                                                                            
                                                                                for (std::size_t j = 7; j > 0; --j)
                                                                                {
                                                                                    local[j] = local[j - 1];
                                                                                }
                                                                                local[4] += tmp2;
                                                                                local[0] = tmp1 + tmp2;
                                                                            }
                                                                            
                                                                            for (std::size_t i = 0; i < 8; ++i)
                                                                            {
                                                                                m_digest[i] += local[i];
                                                                            }

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

                                                                              А они тут и не нужны. Кроме вот этих мест:
                                                                              for (int[0:7] j = 7; j > 0; --j)
                                                                                  {
                                                                                      local[j] = local[j - 1];
                                                                                  }
                                                                              
                                                                              
                                                                              for (int[0:7] i = 0; i < 8; ++i)
                                                                              {
                                                                                  m_digest[i] += local[i];
                                                                              }

                                                                              Хотя с такой системой типов можно просто писать auto и компилер сам догадается. Цикл for — самое простое, где нужна такая типизация.

                                                                              Подсказки программиста нужны в случае работы с массивом через функции.

                                                                              Всякие find, rfind, find_first_of, find_last_of получающие индексы

                                                                              А за ним substr, принимающие эти индексы.

                                                                              Как С++ компилятор что-то может доказать в этом случае?

                                                                              У меня godbolt висит, пока не могу продемонстрировать.
                                                                              Ответить
                                                                              • > Кроме вот этих мест
                                                                                Зачем? Зачем? Такой вид цикла — это самый простой кейс для компилятора. Корректно вывести эти вот [0:7] ([1:7], кстати говоря) сможет даже пхп-обезьяна.

                                                                                > Цикл for — самое простое, где нужна такая типизация.
                                                                                Как раз в цикле for она вообще не нужна.

                                                                                > Всякие find, rfind, find_first_of, find_last_of получающие индексы
                                                                                А их в любом случае надо проверять в рантайме на std::string::npos. По моим экспериментам, пока что «gcc» не догадывается, что «str.find(...) != npos» эквивалентно «find(...) < str.size()». Но для доказательства конкретно этого факта никакие явные «[0..N]» не нужны.
                                                                                Ответить
                                                                                • >А их в любом случае надо проверять в рантайме на std::string::npos.

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

                                                                                  Я о другом твержу.
                                                                                  Если мы передали в substr(int [0:N],int [0:N]) рандомный int — программа выдаёт ворнинг/ошибку
                                                                                  Если мы передали результат find, rfind, find_first_of, find_last_of, программа компилируется без дополнительных проверок.

                                                                                  >Корректно вывести эти вот [0:7] ([1:7], кстати говоря) сможет даже пхп-обезьяна.

                                                                                  Может пхп-обезьяна и сможет, а gcc, как видим не способен.
                                                                                  И clang тоже
                                                                                  $ clang++ -Wall -Wextra bounds.cpp 
                                                                                  bounds.cpp:32:66: warning: unused parameter 'chunk' [-Wunused-parameter]
                                                                                      void process_full_chunk(const std::array<uchar, chunk_size>& chunk) noexcept
                                                                                  Ответить
                                                                                  • > Мы уже говорили, что это должны делать паттерн-матчинг и продвинутая типизация.
                                                                                    Это в любом случае будут явные рантайм-проверки. Абсолютно в любом — пока в поиске участвуют строки, не известные на момент компиляции.

                                                                                    > Может пхп-обезьяна и сможет, а gcc, как видим не способен.
                                                                                    Значит надо выкинуть «gcc» с «clang» и написать нормальный компилятор, который будет способен. Явное обозначение [1:7] не даст компилятору никакой новой информации.
                                                                                    Ответить
                                                                                  • Я, кстати, за «std::array»: https://gcc.godbolt.org/z/YLySg4 (я там в main() добавил использование олгоритма, а то компилятор просто выкидывал всё нахуй).
                                                                                    <source>: In member function 'void Sha256Algorithm::process_full_chunk()':
                                                                                    
                                                                                    <source>:62:26: warning: 'void* __builtin_memmove(void*, const void*, long unsigned int)' forming offset [32, 51] is out of the bounds [0, 32] of object 'local' with type 'std::array<unsigned int, 8>' [-Warray-bounds]
                                                                                    
                                                                                       62 |                 local[j] = local[j - 1];
                                                                                    
                                                                                    <source>:41:38: note: 'local' declared here
                                                                                    
                                                                                       41 |         std::array<std::uint32_t, 8> local;
                                                                                    
                                                                                          |                                      ^~~~~

                                                                                    Как видно, тут внутри у компилятора происходят какие-то диапазонные проверки, очень похожие на то, что ты предлагаешь реализовать явно.
                                                                                    Ответить
                                                                                    • >Это в любом случае будут явные рантайм-проверки. >Абсолютно в любом — пока в поиске участвуют строки, не известные на момент компиляции.

                                                                                      Нет. В этом вся идея. Это герметичная система, для произвольного N.
                                                                                      find вернёт [0,N], а substr примет этот же диапазон [0,N].
                                                                                      Это инвариант для строк произвольной длины.
                                                                                      Потому явные проверки в данном случае НЕ НУЖНЫ.

                                                                                      >Я, кстати, за «std::array»
                                                                                      Я тоже :)
                                                                                      https://govnokod.ru/26515#comment535136

                                                                                      Я же говорю: кресты уже довольно близки к такой типизации. Думаю на шаблонах можно сделать эти диапазоны. Для статических строк уж точно.
                                                                                      Ответить
                                                                                      • Хорошо, подойдём с другой стороны. Что вернёт find, если в строке не найдено искомой подстроки?
                                                                                        Ответить
                                                                                        • >Что вернёт find, если в строке не найдено искомой подстроки?

                                                                                          Уже говорили выше (https://govnokod.ru/26515#comment535141)
                                                                                          Если не найдено возвращается тип NotFound.
                                                                                          В случае если найдено: тип int[0,N-M].
                                                                                          Где M — длина искомой подстроки.

                                                                                          Это логическое продолжение идеи жёстких компайл-тайм проверок.
                                                                                          Ответить
                                                                                          • Значит, во время выполнения будет рантайм-проверка какого-то флага типа или чего-то подобного. Но она будет. Принципиально это ничем не отличается от текущей ситуации, когда в результате find может быть либо npos, либо [0, N - M].
                                                                                            Ответить
                                                                                            • >Значит, во время выполнения будет рантайм-проверка какого-то флага типа или чего-то подобного.

                                                                                              Смешиваются 2 разные ситуации:
                                                                                              * ситуация ненайденной подстрокой
                                                                                              * ситуация с некорректно вычисленным индексом, вылазящим за границы

                                                                                              >Принципиально это ничем не отличается от текущей ситуации, когда в результате find может быть либо npos, либо [0, N - M]

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

                                                                                              А разрешит только использовать только легальные индексы с типом вмещающимся в [0, N].

                                                                                              >find может быть либо npos, либо [0, N - M].
                                                                                              Проверка индекса на npos не гарантирует, что мы не прибавим к нему чего-то лишнего и не вылезем за пределы массива.
                                                                                              Ответить
                                                                                              • > А разрешит только использовать только легальные индексы с типом вмещающимся в [0, N].
                                                                                                А зачем нам для этого явная предикатная система типов?
                                                                                                Компилятор и так знает, что find() может вернуть либо npos, либо [0, N - M]. Ему для этого не надо явно что-то указывать.
                                                                                                Компилятор и так знает, проверили ли мы результат find() на npos, или нет. Ему для этого не надо явно что-то указывать.
                                                                                                На основании этих джвух знаний компилятор уже может доказать, что мы помещаем в substr() валидный/невалидный индекс.

                                                                                                > Проверка индекса на npos не гарантирует, что мы не прибавим к нему чего-то лишнего и не вылезем за пределы массива.
                                                                                                А это должен гарантировать компилятор. Он знает, что если мы проверили индекс на npos, то в индексе будет [0, N - M] — потому что ничего другого там быть уже не может. Из этого он может вывести всё то же самое, что он смог бы вывести из явной предикатной системы типов.
                                                                                                Ответить
                                                                                                • >А зачем нам для этого явная предикатная система типов?

                                                                                                  Она должна быть как минимум под капотом.
                                                                                                  Нам же она нужна для определения своих типов.

                                                                                                  Вроде typedef UserName String<[1..32]> означающего что имя пользователя содержит хотя бы один символ, но не более 32х (например у нас в бд поле nchar(32)).

                                                                                                  Тогда пример с capitalize не будет требовать никаких доп. проверок.
                                                                                                  Т.к. сам тип гарантирует наличие первого символа.
                                                                                                  def capitalize(string):
                                                                                                      string[0] = to_upper(string[0])
                                                                                                  ...
                                                                                                  Ответить
                                                                                                  • > Она должна быть как минимум под капотом
                                                                                                    Разумеется, я не говорю, что предикатная система вообще не нужна — это мощный инструмент, позволяющий делать крутые вещи. Моя позиция в том, что она (в явном виде) не требуется конкретно для создания языка, в котором невозможен выход за границу массива. Я вообще против того, чтобы писать что-то, что за меня спокойно может написать компилятор.

                                                                                                    > typedef UserName String<[1..32]>
                                                                                                    Да, это реальный и нужный пример.
                                                                                                    Ответить
                                                                                                    • А я не говорю что эти типы должен повсеместно писать программер.

                                                                                                      Наоборот, я повторяю что вся концепция должна быть на 90% под капотом (https://govnokod.ru/26502#comment533594).
                                                                                                      Согласен что в большинстве случаев эти типы никак не должны себя проявлять, пока не случится ошибка. Большинству людей хватит обычного for (auto x:[0..7]).

                                                                                                      >для создания языка, в котором невозможен выход за границу массива

                                                                                                      Тогда непонятно как собирать вместе различные модули, связанные интерфейсами и заголовочными файлами.
                                                                                                      В сигнатурах apiшек таки придётся их указать.
                                                                                                      Ответить
                                                                                                      • > Тогда непонятно как собирать вместе различные модули, связанные интерфейсами и заголовочными файлами.
                                                                                                        Да, вот это реальная проблема. В этом случае без специальных сигнатур никак не обойтись.
                                                                                                        Ответить
                                                                                                • >Компилятор и так знает, что find() может вернуть либо npos, либо [0, N - M]. Ему для этого не надо явно что-то указывать.

                                                                                                  Проблемы начнутся при линковке модулей.
                                                                                                  Когда некая функция описанная в .h файле принимает какой-то массив размера N, и возвращает int.
                                                                                                  Без указания явной связи компилятор ничего не сможет сделать.
                                                                                                  Ответить
                                                                                    • >Как видно, тут внутри у компилятора происходят какие-то диапазонные проверки

                                                                                      У меня 9ый gcc, и 9й шланг.
                                                                                      Никаких ворнингов они не выдают.

                                                                                      > очень похожие на то, что ты предлагаешь реализовать явно

                                                                                      То есть тот ворнгинг самый писк прогресса, который gcc начал поддерживать только в trunke. Раньше такого не было.

                                                                                      https://gcc.godbolt.org/z/juLVXg
                                                                                      Ответить
                                                                                      • Именно поэтому я и предложил выкинуть гцц со шлангом и написать нормальный компилятор.
                                                                                        Ответить
                                                                                        • >Именно поэтому я и предложил выкинуть гцц со шлангом и написать нормальный компилятор.

                                                                                          https://xkcd.com/927
                                                                                          Нужно быть реалистом.

                                                                                          Но хорошо что Аллах услышал мои молитвы, и они наконец-то начали пилить range проверки, пусть даже под капотом.
                                                                                          Ответить
                                                                                          • Так мы тут теоретизируем, нам можно всё :-).

                                                                                            > и они наконец-то начали пилить range проверки, пусть даже под капотом.
                                                                                            Вот, я тоже целиком и полностью за это.
                                                                                            Ответить
                                                                            • Я поправил 66 строчку, чтобы она вылазила за границы массива:
                                                                              for (std::size_t j = 12; j > 0; --j)
                                                                              https://ideone.com/d2i4Ft

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

                                                                              Компилирую. Получаю ровно 1 ворнинг, никак не связанный с кривым индексом.

                                                                              $ g++ -Wall -Wextra bounds.cpp 
                                                                              bounds.cpp: In member function ‘void Sha256Algorithm::process_full_chunk(const std::array<unsigned char, 64>&)’:
                                                                              bounds.cpp:32:66: warning: unused parameter ‘chunk’ [-Wunused-parameter]
                                                                                   void process_full_chunk(const std::array<uchar, chunk_size>& chunk) noexcept
                                                                              Почему-то даже в самом тривиальном примере (статический массив фиксированного размера и цикл в том же скоупе) компилятор не смог найти неверную индексацию за пределами массива.

                                                                              Какой анскилл )))
                                                                              Ответить
                                                                              • Я нигде не говорил, что современные крестовые компиляторы соответствуют моему предложению. Своим реальным примером я лишь продемонстрировал, что у них есть потенциальная возможность запилить такую систему.

                                                                                К сожалению, крестовый стандарт наоборот противодействует созданию подобной надёжной системы: с формальной точки зрения, ты написал UB, а потому компилятор имеет право делать абсолютно что угодно, в том числе и молчать как рыба.
                                                                                Ответить
                                                                  • Пойду посплю, завтра [сегодня] продолжим обсуждение. Спокойной ночи.
                                                                    Ответить
                                                            • Точнее не так. Я перевёл код без ifa.

                                                              А с ifом наверное будет так:
                                                              name = getUserInput()
                                                              if len(name) is [1..MAX_INT] {
                                                                  name[0] = to_upper(name[0])
                                                              }
                                                              Ответить
                                                              • В случае если компилятор не может вывести тип из len
                                                                String[0,N] name = getUserInput()
                                                                if N is [0..MAX_INT] {
                                                                    name[0] = to_upper(name[0])
                                                                }
                                                                
                                                                if N is [42..MAX_INT] {
                                                                    name[42] = koko(name[42])
                                                                }
                                                                Ответить
                                                • >Крестокомпилятор до сих пор и компайл-тайм деление на ноль отловить не всегда может, а ты замахиваешься на настолько грандиозные вещи

                                                  Проблема в другом.
                                                  В современных ЯВУ нет краеугольного камня для этой системы: целых типов с диапазонами.

                                                  Остальное дело техники.

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

                                                  Или передаём связанный индекс и строку.

                                                  int[0..N] indexOf(string[0..N] str);
                                                  substring(string[0..N] str, int[0..N] from);

                                                  Сишники 40 лет, как Моисей, таскают за массивами их размеры и ничего.
                                                  В крестах тоже везде торчит размер std:array<int,size_t>.
                                                  Ответить
                                                  • > В современных ЯВУ нет краеугольного камня для этой системы: целых типов с диапазонами.
                                                    Потому что такая система проверки индексов прекрасно работает для строк и индексов, известных во время компиляции. Как только в неё попадает рантайм — всё рушится и сводится к обычному коду.

                                                    > Любая вложенность по сути сводится к тому
                                                    Это всё частные случаи. Проблема останова никуда не девается.

                                                    > Сишники 40 лет, как Моисей, таскают за массивами их размеры и ничего.
                                                    Ну, не совсем ничего. Благодаря этому мы эти же самые 40 лет имеем максимально опасные уязвимости.
                                                    Ответить
                                                    • > имеем максимально опасные уязвимости.

                                                      Это да. Однако я имел ввиду проблему синтаксиального оверхеда подобной затеи. Во всех местах придётся указывать диапазоны возвращаемых интов.


                                                      >Как только в неё попадает рантайм — всё рушится и сводится к обычному коду.

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

                                                      Подобное предикативное доказательство проделывают в продвинутых функциональных языках типа. wvxvw очень давно пояснял.
                                                      Ответить
                                                      • > Во всех местах придётся указывать диапазоны возвращаемых интов.
                                                        В результате для 99% методов это будет «int[0..N]» с ручными проверками. Зачем? Зачем?

                                                        > Тип индекса привязан к типу размера массива.
                                                        Мы говорим о статической или динамической типизации? Я так понял, что о статической. Тогда типы что массивов, что индексов, будут равны «*[0..N]», и смысл всей этой затеи ускользает.

                                                        Можно всё проделать гораздо проще: заставить компилятор доказывать, что любая индексация корректна. Не может доказать — показывает ошибку/предупреждение. По своей сути это ничем не будет отличаться от системы типов с диапазонами, доказывать надо будет абсолютно то же самое, зато программисту не надо будет плодить плохочитаемый бойлерплейт.
                                                        Ответить
                                    • >В этом смысле Optional с жёсткими компайл-тайм проверками гораздо лучше простого инта.

                                      i=array.indexOf(element);
                                           i+=1;
                                           x=array[i]
                                      In file pituh.koko:146:
                                      ERROR: проснись, друг, ты обосрался
                                                       x=array[i]
                                      Compilation error^


                                      И соответственно ifPresent с ебаными лямбдами нахуй не нужен.
                                      Есть же (ну или скоро будет) в шарпе/яве/крестах паттерн матчинг.

                                      https://openjdk.java.net/jeps/305

                                      Вот возвращать подтипы
                                      var i=str.indexOf();
                                      if (i instanceof Index indx){
                                         str.at(indx);
                                      }else if (i instanceof NotFound){
                                      ...


                                      А Optional — говнище ебаное.
                                      Ответить
                                      • > In file pituh.koko:146:
                                        Это не поможет, если indexOf вернёт индекс последнего элемента. Или ты предлагаешь запретить вообще любой непроверенный доступ к массиву (изменил индекс — изволь делать if idx < arr.len)?

                                        > Вот возвращать подтипы
                                        А это уже то, о чём гуест8 и я писали в самом начале ветке.
                                        Ответить
                                        • >Или ты предлагаешь запретить вообще любой непроверенный доступ к массиву (изменил индекс — изволь делать if idx < arr.len)?

                                          Не совсем.
                                          Любой вылезший за тип и недоказанный компилером.

                                          > var i=s.indexOf("koko") //[0,N-1-4]
                                          > //к i можно прибавить число [0,4], чтобы не вылезти за пределы.

                                          Другой пример
                                          var i=arr.indexOf(3) //[0,N-1]
                                          //к i можно безопасно прибавить тип [0,0]. То есть сложить с нулём.


                                          >Это не поможет, если indexOf вернёт индекс последнего элемента

                                          Длина строки N.
                                          indexOf() возвращает последний элемент N-1, это значение находится в диапазоне [0, N-1] — соответственно если индекс не менять, то по нему смело можно обращаться к массиву.


                                          >изменил индекс — изволь делать if idx < arr.len
                                          Не if а скорее явный clamp-каст в тип [0,N-1]
                                          Ответить
                            • «И мы опять приходим к тому, о чём я говорил...»
                              Iterable[Image] images = images.findByName('petooh');
                              for (Image i:images)
                                  printer.print(i);  // ОК

                              Плюс это расширябельно. Завтра могут быть 2 картинки с одинаковым именем, и все вызовы метода придётся переписывать.

                              Хипстеры тоже будут довольны:
                              images.findByName('petooh').forEach(i->printer.print(i))
                              Ответить
                              • А это уже будет тормозить, потому что зерокост коллекций на горизонте не видно.
                                Вдобавок, в некоторых случаях это нарушает семантику. Метод, по определению возвращающий ноль или один элементов (какой-нибудь «getById»), и при этом в сигнатуре имеющий Iterable, выглядит крайне странно и запутывает читающего.
                                Ответить
                                • >А это уже будет тормозить, потому что зерокост коллекций на горизонте не видно.
                                  Ээээ. Ну неправда же.
                                  В жабе их полно. Кост у них такой же как у Optional.

                                  Навскидку:
                                  https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#singletonList(T)
                                  https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#singletonList(T)
                                  Ответить
                                  • > такой же как у Optional
                                    Дык я не про жабовское анскильное гуано, а про гипотетический зерокост (в крестовом стиле) Optional с жёсткими компайл-тайм проверками (конпелятор выдаёт ошибку, если не может доказать, что на момент доступа к содержимому оно было явно проверено на существование).
                                    Ответить
                                    • >а про гипотетический зерокост (в крестовом стиле)
                                      Ну в крестовом стиле и гипотетические коллекции могут быть зеро-кост.

                                      List[0,1]<Image> //пустой список, либо список с 1м элементом
                                      
                                      List[0,10]<Image> //список содержащий не более 10 элементов

                                      Горячо любимый мною std::array довольно близок к этому.
                                      Ответить
                                      • > List[0,1]<Image>
                                        А это и есть обобщённый Optional, и главный* его недостаток — необходимость многость рефакторить при изменении количества возвращаемых элементов — никуда не девается. Даже в случае с List[0,10].
                                        А вот обобщённая коллекция потребует либо кучи, либо дополнительных проверок, что уже не зерокост и вообще тормозная питушня.
                                        Ответить
                              • if (images.length > 0) printer.print(images[0])

                                Перепесал, проверь.
                                Ответить
                                • О, уважаю, братишка.

                                  Но я уже сказал выше:
                                  https://govnokod.ru/26515#comment535130
                                  >А сам список можно представить в виде массива.

                                  Впрочем for~each и на массивах работает
                                  for (Image i:images)//still works
                                      printer.print(i);
                                  Ответить
                        • >db.getUser(username, password) -> Optional<User>

                          Ну блядь верни List, Collection, Iterable наконец. С одним элементом или пустой.
                          Не хочу, хочу жрать «Optional»

                          Iterator<User>it  =  db.getUser()....
                          it.hasNext() ? doSomething(it.next()) : whenEmpty()

                          Уже есть все нужные абстракции.

                          Iterable — тот же ленивый список из Хасцеля. С поддержкой for~each из коробки.
                          Ответить
                          • Я привёл этот пример в качестве хуёвого архитектурного решения, крайне простого в реализации, но сложного в дальнейшей поддержке.

                            Впрочем, возврат коллекции тут совершенно избыточен и ничем не отличается от Optional: мы в любом случае теряем информацию о том, что пошло не так. Узнать, ввёл ли пользователь неправильный пароль или неправильный логин, не получится. Разумеется, это синтетический пример, и в реальности проверку пароля следует вынести в отдельное место.
                            Ответить
                            • >Впрочем, возврат коллекции тут совершенно избыточен и ничем не отличается от Optional

                              Именно! Я о том, что в жабе уже был один тип, покрывавший все эти нужды. Зачем что-то ещё — непонятно.

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

                              Но с Iterable хотя бы можно унифицировать код, и при необходимости возвращать несколько юзеров не придётся ебать мозги рефакторингом и конверсией с Optional и в глисты и коллекции.
                              Ответить
                              • Да, логично. При наличии null и пустых коллекций отдельный тип Optional без компайл-тайм проверок (см. выше) — избыточная и ненужная хрень.
                                Ответить
                                • показать все, что скрытоvanished
                                  Ответить
                                  • Какой? Теоретический, предлагаемый мной — тем, что нельзя получить доступ к пустому значению. Тот, который сейчас в жабах/питонах/etc — ничем.
                                    Ответить
                                  • >чем вообще опшинал лучше нула?

                                    Возможностью положить его в некоторые мапы/кеши, которые не поддерживают null-values.

                                    А реально ничем.
                                    Ответить
                                    • > Возможностью положить его в некоторые мапы/кеши, которые не поддерживают null-values
                                      Ну или так, да. Хотя, честно говоря, это выглядит как проблема в соответствующих мапах/кешах, а не в null-е.


                                      Кстати, в «C++» нельзя положить в вектор ссылку:
                                      int a = 42, b = 13;
                                      std::vector<int&> vec = {a, b};

                                      Такой код генерирует простую и понятную ошибку на жалких 20000 символов: https://pastebin.com/SGeh4X19.
                                      Чтобы исправить это досадное упущение, в Стандарт запихнули некий «std::reference_wrapper», находящийся — что очевидно любому здравомыслящему человеку — в заголовочном файле <functional>:
                                      int a = 42, b = 13;
                                      std::vector<std::reference_wrapper<int>> vec = { a, b };
                                      vec[0].get() = -1;
                                      std::cout << a << std::endl;  // -1


                                      Удобно, правда?
                                      Ответить
                                      • Сразу видно, что «C++» изобрели шахматисты.
                                        Ответить
                                      • >Такой код генерирует простую и понятную ошибку на жалких 20000 символов
                                        Пастбин не открылся. Но поверю на слово :)

                                        Какой пиздец )))

                                        PS Воистину пиздец:
                                        $ g++  t.cpp 2>&1  | wc -l
                                        190
                                        $ clang++  t.cpp 2>&1  | wc -l
                                        111
                                        Ответить
                                        • Не нужно 20000 символов, достаточно одного хорошего вдоха. Апчхи! Хр-р-р!
                                          Ответить
                        • Так про что угодно можно сказать.

                          Не ебашьте Optional там, где вам важна ошибка
                          Ответить
                          • Далеко не про что угодно.

                            Optional опасен тем, что он подталкивает программиста писать говно. Точно так же, например, как пресловутое goto, которое тоже в очень редких и специфических случаях полезно и нужно, а во всех остальных приводит к нечитаемой лапше.
                            Optional — это инструмент, который может использовать только человек с сильной волей и железной дисциплиной. Иначе получается говно.
                            Ответить
                            • Optional говно, потому что какое-то петухи не осилили семантику?

                              Ну так нахуй петухов, в общем-то.
                              Ответить
                              • Optional говно, потому что он подталкивает программиста писать говно.

                                > Ну так нахуй петухов, в общем-то.
                                До тех пор, пока тебе не придётся иметь дело с кодом, написанным другим человеком.
                                Ответить
                                • Я каждый день имею дело с кодом, написанным другим человеком. Проблем куча, но с Optional - ни одной. ЧЯДТН?
                                  Ответить
                                  • Значит, ты их просто не замечал. Или же тебе повезло, и другой человек понимает, что, образно выражаясь, Optional considered harmful. Или, как вариант, ты не видишь проблемы в сообщении об ошибке вида «что-то пошло не так».
                                    Ответить
                                    • Нет, серьёзно, есть какие-то рациональные причины отказаться от Optional прямо сейчас?
                                      Ответить
                                      • Есть рациональные причины обращать повышенное внимание на методы, возвращающие Optional. Такие методы уместны тогда и только тогда, когда причина возврата null может быть только одна, и нет никаких предпосылок, что в будущем не появится ещё какой-то причины. Просто потому, что любой Optional — это потеря информации об ошибке. В случаях, когда эта информация может быть точно восстановлена (collection.find('petooh')) — Optional уместен (но всё равно остаётся проблема рефакторинга, о которой говорил дядя ПИ).
                                        В дополнение, есть рациональные причины выкинуть Optional из новых языков программирования (о чём мы тут и развели дискуссию). Именно поэтому я за «Go».
                                        Это ничем не отличается от ситуации c goto.
                                        Ответить
    • Что это за жабовский высер ?
      Ответить
    • Java это ссаная вореация на Java Script и не более того - я так считаю !
      Ответить

    Добавить комментарий