1. JavaScript / Говнокод #27855

    +1

    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
    function main() {
        let a: number | string;
    
        a = "Hello";
    
        if (typeof(a) == "string")
        {
    	print("str val:", a);
        }
    
        a = 10.0;
    
        if (typeof(a) == "number")
        {
    	print("num val:", a);
        }
    
        print("done")
    }

    Аллилуйя братья... я вам принес "union"-s . Возрадуйтесь новой фиче. (А ты можешь так в с/c++?)

    дампик https://pastebin.com/QNmKFfT7

    C:\temp>C:\dev\TypeScriptCompiler\__build\tsc\bin\tsc.exe --emit=jit --opt --shared-libs=C:\dev\TypeScriptCompiler\__build\tsc\bin\TypeScriptRuntime.dll C:\temp\1.ts 
    str val: Hello
    num val: 10
    done

    Запостил: ASD_77, 06 Декабря 2021

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

    • ну и где коменты?
      Ответить
    • коменты хочу
      Ответить
    • std::variant<double, std::string> x;

      if (std::string* p = std::get_if<std::string*>(&x)) { ... }
      Ответить
      • для этого мне надо с++ 20 ставить :)?
        Ответить
        • 17. Но в принципе можно и 11.
          Ответить
          • помоему 11 не тянет это чудо
            Ответить
            • А тьфу, и правда 17... Ну да похуй, из буста возьмёшь.
              Ответить
            • Можешь из буста спереть, там, по-моему, ещё для 98 было.
              Ответить
              • > для 98

                Скопипастим-ка эту портянку ещё 20 раз... Или у них boost preprocessor уже тогда работал?
                Ответить
          • можно и в борланд C++ 3.1: работай только через указатель и RTTI. В примере с TS ведь сделано на самом деле именно так
            Ответить
            • у меня RTTI минимальный ... если его можно так назвать. скорее это TaggedUnion
              Ответить
    • Было бы еще хорошо добавить нормальную реализацию tagged union-ов (как в Расте например). А не: type MySumType = { kind: 'a'; fieldA: string } | { kind: 'b'; fieldB: boolean } .
      Ответить
      • Ну это будет уже не TS...

        Они же пытались очень тонкую прокладку для проверки типов сделать, а не новый язык. Видимо поэтому и не могут добавить какие-то скрытые магические поля в структуры.
        Ответить
        • как раз в TS есть такое
          Ответить
          • Что есть?
            Ответить
            • https://www.typescriptlang.org/docs/handbook/2/narrowing.html Discriminated unions
              Ответить
              • и где здесь "т добавить какие-то скрытые магические поля в структуры."?

                Это касается только проверки типов. В результирующем JS ничего этого нет
                Ответить
                • в этом и прикол... что первое поле это и есть твое любимое магическое поле. которое имеет один и тот же тип для разных вариантов
                  Ответить
                  • Но оно ведь есть в js'е и называется точно так же? Т.е. не такое уж оно и магическое.
                    Ответить
    • Кстати, конпелятор все переменные обозначает magic tagged union-ами? Упадет ли на стадии компиляции код типа?

      let a: number = 4;
      typeof(a);
      Ответить
      • Хм, а здесь ведь тип просто number?
        Ответить
        • На уровне юзера - да. На уровне kernel-а ЖС - в общем случае tagged union (а вы как думали засчет чего динамическая типизация?). Но ведь ОП пилит конпелятор диалекта TypeScript в машинный код, там обычно принято не включать инфу о типе для встроенных типов.
          Ответить
          • В крестах тоже не принято, тем не менее typeof() там вполне работает для встроенных типов.

            З.Ы. Если их тип статически известен, конечно. Из void* он тип уже не вытрясет.
            Ответить
            • Так в крестах typeof() для встроенных типов — чисто компайл-тайп. В рантайме он работает только для вротуальных классов.
              Ответить
              • Что мешает в данной реализации TS поступить точно так же? Раз тип нам уже известен статически, то его и вернём.
                Ответить
                • Шаблонов не завезли.
                  function foo(a: int | float) {
                      console.log(typeof(a));  // Не получается статически, насяльника.
                  }
                  foo(42);
                  foo(16.16);
                  Ответить
                  • Дык в данном случае тип есть в рантайме и будет работать нормальный рантайм typeof().

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

                    Конкретной реализации ASD конечняо ничего не мешает
                    Ответить
                  • тут получается т.к. за крышкой TaggedUnion

                    function foo(a: int | float) {
                    console.log(a); // Не получается статически, насяльника.
                    // а теперь получается начальника
                    if (typeof(a) == "number") console.log(a); // мы знаем, что в коде после typeof(a) 100% number
                    }
                    foo(42);
                    foo(16.16);
                    Ответить
            • typeof поди макрос/конструкт шаблона. В ЖС typeof именно оператор, выполняющийся в рантайме.
              Ответить
              • Но мы же конпелятор пишем. И он может раскрыть этот оператор и заранее подставить его результат. Тогда в рантайме можно такую переменную ничем не обмазывать.

                Тут главное в рантайме не дать string засунуть в этот number потом... Но я думаю это учтено.
                Ответить
          • >На уровне kernel-а ЖС - в общем случае tagged union (а вы как думали засчет чего динамическая типизация?).

            мы думали, что там говно в куче лежит, и динамически диспатчится по указателю
            Ответить
            • Зависит от реализации, наверное. Tagged union выглядит логичнее т.к. типов конечное число. Классов то нету.
              Ответить
              • сейчас нету, а завтра есть.
                Этоже только в TS ты можешь скзаать
                let a: number | string;

                а в JS этого знания нет.

                алсо, как там хранится строка?
                Ответить
              • А объекты уже сами хранят, какой функцией они были созданы. Переменная хранит только тег "указатель на кучу" и адрес объекта в куче.
                Ответить
            • Это уже лет 10 как минимум неактуально для ЖС-а. По-простому любая переменная в ЖС (без всяких JIT-компиляций) имеет тип:

              enum Variable {
              Number(uint64),
              String{ length: int64; begin: *char },
              Object(*void),
              Boolean(bool),
              // ...
              }

              На самом деле встроенных "типов" (различающихся по memory layout в т.ч.) больше

              https://v8.dev/blog/elements-kinds
              Ответить
              • переменная имеет вид перечисляемого типа?

                Или всмысле там юнион из uint64в котором может быть number, указатель на объект, bool, и указатель на строку с длиной на перевес?

                В общем как я и сказал почти:
                > говно в куче лежит, и динамически диспатчится по указателю
                только с исключением на Number и Boolean (строка тоже в куче)

                Документацию и пейперы лучше распостранять текстом. Если там очередное тикток видео на полтора часа, то оно не нужно
                Ответить
                • Ну да, буду щас копипастить простыню, вместо того чтобы сослаться на нее...

                  > Или всмысле там юнион из uint64в котором может быть number, указатель на объект, bool, и указатель на строку с длиной на перевес?

                  Скорее структура, которая хранит char-тег и юнион из uint64 (= number), указателя на объект, булеана, view для строки (сама строка лежит в куче) и пр.

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


                    если бы она была текстом, то проблем бы не было. Видео на полтора часа неприлично предлагать:)

                    >Скорее структура

                    Ну да, я понял: у нас есть 64 бита, в них мы храним булен, намбр, указатель итд, смотря что за сущность у нас.

                    Так что typeof можно получить довольно быстро, действительно. А вот за instanceof придется ходить в объект вероятно по указателю
                    Ответить
                    • > Так что typeof можно получить довольно быстро, действительно. А вот за instanceof придется ходить в объект вероятно по указателю

                      Да, информация о классе/функции создавшей объект, хранится уже внутри объекта. Так впрочем ведет себя и Джава с Шарпом (иначе невозможна была бы рефлексия).

                      > если бы она была текстом, то проблем бы не было. Видео на полтора часа неприлично предлагать:)

                      По ссылке не только видео на полтора часа. Но ладно.
                      Ответить
                      • >Да, информация о классе/функции создавшей объект, хранится уже внутри объекта.

                        Угу. В JS есть типы boolean/string/итд/object, а уже object может быть инстансом класса. Информация о типе хранится прямо в юнионе, а о классе уже по ссылке


                        > Так впрочем ведет себя и Джава с Шарпом (иначе невозможна была бы рефлексия).

                        В C# есть value types структуры, их тип можно понять статически, но смысла в этом обычно нет.
                        Ответить
        • И если переменная a и на уровне kernel-а number, то вызов typeof вообще не валидный с точки зрения компилятора (упадет компиляция либо вызов функции вернет черт знает что).
          Ответить
          • валидный просто его значение известно зарание. typeof это не функция это конструкция языка
            Ответить
      • нет не упадет .. тк. компилятор тупо заменит typeof(a) на константу "number"
        Ответить
        • А при передаче в функцию, которая примает number | string, компилятор завернёт "a" в tagged union?
          Ответить
          • верно...
            Ответить
            • А я чет сначала подумала про то, что компилятор генерит портянку для каждого набора generic аргументов. Типа

              function f(x: number | string, y: Date | number) { ... }
              function main() {
                f(4, new Date());
                f("xabc", 10000000000000000);
              }


              Превращается перед компиляцией в байт-код во что-то типа:

              function f$x_number$y_Date(x: number, y: Date){ ... }
              function f$x_string$y_number(x: string, y: number){ ... }
              
              function main() {
                f$x_number$y_Date(4, new Date());
                f$x_string$y_number("xabc", 10000000000000000);
              }


              (как с плюсовыми шаблонами примерно)
              Ответить
              • Кобенаторный взрыв.
                Ответить
                • Судя по тому, что typeof - компайл-тайм конструкт, такое предположение возникло. Иначе что должно быть сгенерированно вместо typeof(a) внутри функции, если какого конкретно субтипа будет a в общем случае известно только в рантайме.
                  Ответить
                  • Ну собственно рантайм проверка, которая смотрит какой из вариантов там реально лежит...
                    Ответить
                • Или typeof от аргумента функции внезапно не компайл-тайм конструкт а оператор вычисляющий по магическому полю аргумента строковый литерал? Интересная механика...
                  Ответить
                  • Компайлтайм конструкт, который раскрывается в рантайм проверку.
                    Ответить
                    • Компайл-тайм - это значит результат typeof(a) вычисляется во время компиляции. В рантайме if-ы с typeof будут просто болванками.
                      Ответить
                      • Или оптимизатор их вообще выбросит, если очевидно что они не вызовутся.
                        Ответить
                  • Или даже так: если компилятор видит что функция вызывается только с небольшим числом вариантов аргумента, typeof вычисляется в компайл-тайме. И генерятся портянки функций. Иначе generic аргумент имеет тип tagged union-а и typeof - обычная функция, мапящая магическое поле на строку. Комбинаторного взрыва удается избежать.
                    Ответить
                    • Единственно надо учесть, что переменную могут посреди функции присвоить и она сменит тип. Такие функции видимо не стоит оптимизировать.

                      Т.е. например функция принимает int|string, мы в неё передаём int, но она внутри в эту переменную ложит string.
                      Ответить

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