• Делаем таблицу с бесконечной прокруткой без event listener
    0

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


    Видел до 300к строк в таблице. Хотя очевидно, что пользователь в любом случае будет фильтровать, нежели искать по алфавиту (но если забрать у них такую возможность и не предложить ничего другого адекватного, будет кипиш).

  • Tcl для Cisco IOS в простых примерах
    0

    TCL интересный язык. В лиспе всё это списки, в TCL — всё это строки (даже числа).


    Весь синтаксис — выполнение процедуры (где первой строкой её имя, остальными — аргументы), двойные кавычки, формирующие строку с пробелами и позволяющие делать ${подстановку_переменных} и [подстановку вызова процедуры], фигурные кавычки, которые возвращают строку как есть.


    В итоге, когда мы видим if {$x == 5} { ... } else { ... } это на самом деле вызов встроенной процедуры if, где первый аргумент подаётся в процедуру expr, которая умеет брать строки и проверять её на логические условия. А блоки кода — это строки, одна из которых будет за'eval'ена, если условие выполнится.

  • Земля — плоская
    0

    Посмотрите на Military Grid Reference System.


    Это способ записи UTM-координат, который так же делит Землю на прямоугольники:


    Заголовок спойлера

    image


    В итоге координаты выглядят как 4QFJ12345678, где 4QFJ представляет собой квадрат 100км², а цифры — это метры по x/y в данном квадрате.

  • Стеганография мимо файлов: прячем данные прямо в секторах
    0

    Вместо разделения данных по кускам лучше использовать фонтанные коды ( https://en.wikipedia.org/wiki/Fountain_code ). Тогда можно будет шифровать рандом и результат применения кодирования, плюс шансы, что пропадут все куски с одинаковым идентификатором уменьшатся.

  • Три Больших Лжи о JavaScript
    +11

    JavaScript — простое понятие объекта, нормальные замыкания. Плюс прототипы, объясняющие, откуда у объектов/массивов берутся их дефолтные функции. И связка «функция в поле объекта», протаскивающая this.


    На этом основа JavaScript заканчивается и всё остальное — сахар и объекты среды. И в этом его сила — он очень близок к Lisp по минималистичности и возможностям делать что угодно как угодно. Он не навязывает какую-либо «единственно верную» структуру и позволяет реализовывать абстракции, которые в других языках получили бы сопротивление от самого языка.

  • Первый взгляд на JavaScript глазами Java — разработчика
    +1
    Лично я не сторонник слабой типизации, но мой коллега, недавно попробовавший JavaScript, почему-то был в восторге от нее. Возможно, это дело вкуса. А как считаете вы, что лучше слабая или сильная типизация?

    С технологической точки зрения «лучше» конечно же формально верифицированная зависимыми типами программа с понятной человеку спецификацией. Но в реальном мире на получение доказательства корректности «правильного квиксорта» у меня уйдёт пара дней.


    Каждая из статической/динамической сильной/слабой типизаций даёт свои профиты и даёт языкам свои ниши.


    Люди часто наезжают на JavaScript и понятно, за что, он не идеален. Мне нравится статическая типизация. Во-первых чтобы что-то написать, нужно продумать типы, что даёт половину решения и «места, куда писать код». Во-вторых при достаточно хороших типизациях написанный код «просто работает», иногда даже с первого раза. Но чем больше разбираешься с системами типов, тем больше хочется описать, что не всегда возможно. В итоге ушёл в JavaScript, который никогда не мешает и не требуется ничего доказывать компилятору.


    Типы в головах

    image

  • Субъективное видение идеального языка программирования
    +2
  • Как убедить клиента или компанию использовать Flutter
    0
    о компиляции приложения для его запуска на мобилке вообще речи не велось, так как Python запускается из исходников — внес правки в код, нажал "Run" — видишь результат

    Как запуск из исходников связан с восстановлением состояния интерфейса?

  • Зависимые типы — будущее языков программирования
    +1

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


    fun login(name : String, nameIsNotEmpty : Less<0, length(name)>)

    Про то, как может выглядеть Less<a, b> выше приводил пример.


    В итоге чтобы в коде вызвать функцию login, нам нужно туда второй параметр передать с экземпляром Less, который будет гарантировать, что name не пусто. Если попробовать передать что-то не то — не скомпилируется, ибо типы не совпадут.


    Что будет требовать обязательной проверки данных, приходящих от пользователя (например, вызовом того же isLesser(0, length(name)) из примера комментария выше).

  • Зависимые типы — будущее языков программирования
    +3

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


    Чтобы если написать что-то вроде:


    for (int i = 0; i <= arrayLength; i++) sum += array[i];


    Компилятор сказал: звучит неубедительно, i точно всегда будет меньше arrayLength? Докажи. Чего доказать нельзя, опечатка/баг пойман.


    static if в шаблонах это интересно. Но недостаточно, нужно иметь возможность использовать типы с произвольными значениями, не обязательно известными на этапе компиляции.


    Если статические вычисления могут изменять структуру шаблона/типы полей и выполняются на этапе компиляции, то язык определённо не может сделать такой тип:


    int x = read();
    
    Equal<x, x> eq = Equal.refl(x);

    И определять типы функций около таких:


    int get(uint n, int[n] array, uint index, Less<index, n> less)

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


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


    Например:


    type Less<a:uint, b:uint>
        zeroLessThanOne : Less<0, 1>
        incrementBoth : Less<a, b> → Less<a + 1, b + 1>
        incrementRight : Less<a, b> → Less<a, b + 1>

    У нас есть тип «число a меньше числа b». У него есть «конструктор» — «ноль меньше единицы». И две функции для получения всех остальных значений. Больше способов получить экземпляр Less нет. Очевидно, что для всех экземпляров этого типа выполняется a < b.


    uint не переполняется

    Для примера будем считать, что uint у нас бесконечно большой и никогда не переполняется, иначе придётся ещё передавать доказательства того, что правая сторона не равна максимальному значению uint.


    Затем мы можем написать функцию:


    isLesser : a:uint → b:uint → Maybe<Less<a, b>>

    Которая бы взяла Less<0, 1>, сделала Less<a, 1 + a>, если 1 + a меньше b — добила бы до Less<a, b>, иначе вернула nothing.


    Затем мы можем где-то один раз получить значение Less<x, 5>, передавать его во все функции, куда передаём x и безопасно использовать x в качестве индекса по любым массивам длины больше либо равной пяти.


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


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

  • CraSSh: ломаем все современные браузеры вычислениями в CSS
    0

    Нужен shadow dom, которому разработчики веб-приложений могли бы указать что-то вроде «лимит памяти 10мб», чтобы пользовательский контент со стилями не мог съесть вообще всю оперативную память и уронить вкладку целиком.


    А вычисления js/css в основном потоке хорошо бы ограничить в скорости исполнения на уровне браузера (например, 10мс вычисляем, 20мс спим), если превышен лимит в ~100мс времени. Во-первых если оно не уложилось, то в любом случае это надолго. Во-вторых отличная мотивация разработчиков к написанию отзывчивых интерфейсов.

  • Модальное окно, которое вы ждали. Или как вызвать «всплывашку» с разных кнопок на чистом JS
    0

    Потому что когда-нибудь получится:


    div.innerHTML = '<span>' + item.getDescription() + '</span>';

    А в description однозначно проникнет:


    <script src='//example.org/evil.js'></script>

    Где-то по хабру ходили слухи, что у innerHTML производительность лучше

    Тоже видел, не могу найти. Если этот HTML показывает не просто статику, а является куском интерфейса, в любом случае придётся селекторами выбирать из него элементы, чтобы навешивать обработчики/управлять отображением. И тогда cloneNode(true) должен быть быстрее, ибо не нужно парсить строку. По крайней мере в 2009 был.

  • Модальное окно, которое вы ждали. Или как вызвать «всплывашку» с разных кнопок на чистом JS
    +1

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


    В конструкторе вы используете оператор in для проверки наличия опций и записываете их в this. А дальше проверяете эти опции на !== null. Что-то мне кажется, что если не подать classListBg, случится исключение в setClasses. in не нужен, вместо x !== null для таких проверок лучше просто проверять x, либо typeof x === 'string', либо Array.isArray(x), в зависимости от того, что ожидаем. Ещё лучше — хранить в опциях дефолтные валидные значения, использование которых ничего не изменит (вроде пустого списка/нуля/etc), чтобы проверки вообще не были нужны.


    Ваш delegateClick создаёт утечку памяти. Вы подписываетесь на клики по всему окну, но никогда не отписываетесь. Каждый раз, когда я буду создавать экземпляр XMC, я буду подписываться на клики и держать указатель на mf/this, который будет держать всё остальное.


    Хотя и никаких методов для того, чтобы удалить все созданные элементы из DOM тоже нет.


    this.body.innerHTML = this.content;

    Никогда не используйте innerHTML. Кроме случаев, когда HTML небольшой, находится прямо перед глазами и в него ничего не вклеивается. Передавайте всё элементами/фрагментами. Если нужно показать текст — document.createTextNode('text'), если нужна более сложная структура — как-нибудь постройте дерево элементов, выберите узлы и используйте textContent.


    А если я хочу анимацию появления окна? А если у меня в проекте уже есть модуль, отвечающий за оверлеинг интерфейса (то, что у вас делает backgroundLayer)?


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


    Помню тоже как-то на хабр про свой велосипедный PHP-шаблонизатор писал. Не расстраивайтесь, продолжайте абстрагировать.

  • 4 совета для оптимизации webpack-приложения
    0

    Опции babel'а в js-файле. Какие проблемы перед тем как экспортировать объект с опциями пройтись по массиву плагинов и в каждый объект добавить поле?

  • Как сделать поиск пользователей по Github используя VanillaJS
    0

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


    innerHTML += за гранью добра и зла. Вообще, лучше никогда не генерировать HTML руками, ибо можно легко что-нибудь непроэкранировать.


    Вот тут, например:


    '<p class="repository__description">' + repository.description + '</p>'

    В description можно положить <script>, например.

  • Как сделать поиск пользователей по Github используя VanillaJS
    0

    Полифилл, да. Для IE11, к сожалению, тоже нужен полифилл, он в конструктор массив не принимает, можно забыть.


    Версии фиксируются, а документацию/примеры по старым версиям потом сложно искать.


    Я больше боюсь за то, что сборщик может вдруг перестать быть просто сборщиком и начнёт очень сильно влиять на сам код. Вроде влиять на import'ы.


    Если мы начинаем писать код, который не может самостоятельно работать в браузере — это уже не javascript, а какой-то собственный язык. Чем мне заниматься не хотелось бы по религиозным причинам. Хотя тут на вкус и цвет.

  • Как сделать поиск пользователей по Github используя VanillaJS
    0

    Спасибо за отзыв.


    Мне нравится концепция исчезающих фреймворков, возможно, через какое-то время изучу один из них, возможно, Svelte.


    Мне не нравится, что фронтенд-фреймворки это какой-то сплошной хайп, за которым сложно уследить (хотя сейчас устаканилось довольно с реактом/vue/angular). А в итоге мы имеем легаси-проекты на первых ангулярах, потому что раньше это было круто.

  • Как сделать поиск пользователей по Github используя VanillaJS
    0

    У меня всё-таки получилось в три раза больше javascript'а. Если не считать папку lib с «моим фреймворком», получается тот же порядок (разницы 20 строк).


    recompose-github-ui
    recompose-github-ui$ cloc public src
           8 text files.
           8 unique files.                              
           0 files ignored.
    
    -------------------------------------------------------------------------------
    Language                     files          blank        comment           code
    -------------------------------------------------------------------------------
    CSS                              2             21              0            123
    JavaScript                       5             12              0            120
    HTML                             1              6             20             17
    -------------------------------------------------------------------------------
    SUM:                             8             39             20            260
    -------------------------------------------------------------------------------
    

    vanilla-github-habr
    vanilla-github-habr$ cloc .
          14 text files.
          14 unique files.                              
           1 file ignored.
    
    -------------------------------------------------------------------------------
    Language                     files          blank        comment           code
    -------------------------------------------------------------------------------
    JavaScript                      12            115              9            358
    CSS                              1             25              0             67
    HTML                             1             10              0             53
    -------------------------------------------------------------------------------
    SUM:                            14            150              9            478
    -------------------------------------------------------------------------------
    
  • Конкурентность: Асинхронность
    0

    Нет, асинхронность может существовать там, где нет никакого event loop'а. Ведь ничто не мешает передать в любой поток коллбэк, который будет вызван, как что-нибудь произошло.


    Кооперативная многозадачность решённая через event loop просто порождает асинхронность, а не является ей. Асинхронность — просто абстрактное понятие, описывающее процессы, которые происходят не мгновенно.

  • Конкурентность: Асинхронность
    0

    Написал «а внутрь неё подаётся получившееся R, либо возбуждается исключение E», но не уточнил, что исключение прямо внутри корутины возникает, ну и примера нет, спасибо.

  • Конкурентность: Асинхронность
    0

    Тут важно ещё понимать, что «обращение к БД» в основном… не происходит, мы почти всё время просто ждём. Записать/прочитать данные в буффер и отправить его сетевой карте — это относительно быстрая операция, относительно времени, которое проходит между такими операциями. И если у нас времени ожидания достаточно, чтобы ещё повычислять чего-нибудь другого — нам не нужен ещё один поток.

  • Конкурентность: Асинхронность
    0

    Первое зависит от реализации. Платформа, предоставляющая event loop и асинхронное API действительно может создать сколько угодно других потоков и просто перекидывать данные оттуда в «наш главный». Но по факту новые потоки нужны только если что-то действительно вычисляем, опросить состояние IO (пример с БД) можно и в том же самом. А как там ОС это проворачивает — неизвестно, может у неё отдельный поток тоже для всего IO, я не знаю.


    Второе: да, то же самое микропереключение. Только всё состояние живёт в замыканиях функций. Что, по сути, ничем не отличается от поточного контекста. Т.е. если бы мы это делали там, где нет замыканий, нам пришлось бы в функции, запрашивающие старта асинхронной операции, передавать не только коллбэк, но и какой-то объект контекста, который будет передан этому обработчику во время его вызова.

  • Мейнтейнеры не масштабируются
    0
    Обязательно должны быть те, кто будет ревьювить код, вносимый в настолько важный проект и иметь права на блокирование внесения некоторых коммитов. Иначе шансы на получение бэкдоров/прочих радостей значительно возрастают, чего не хотелось бы.

    Я не знаю, как на самом деле устроено ядро и мне вот интересно, почему в ядре настолько много работы и из-за чего её объём может расти? Почему не «закончить» разработку ядра, оставив в нём только совсем стабильные системные абстракции, поддерживать «околоядро», предоставляющее API для взаимодействия модулей, а поддержку оборудования вообще вынести за его пределы? Или оно так и есть? Как оно вообще?
  • Еще один велосипед для борьбы с callback hell в JavaScript
    0
    Выглядит так, будто бы оно просто распарсивает функцию и делает из неё генератор.

    Почему они с тем же успехом не могли распарсивать функцию и впиливать поддержку await/async? Если это так хочется делать в рантайме, а не транспилировать.
  • Tesla начала производство литий-ионных аккумуляторов на своей Гигафабрике
    0

    Почему она цилиндрическая?


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


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


    Или же даже не разделять по слоям, а на самом корпусе аккумулятора сделать канавки для тока воздуха.

  • Конкурентность: Параллелизм
    0
    Лично по мне, пока ты не объяснил какую-либо технологию кому-нибудь ещё — ты её сам не понимаешь.

    Я вот пока не написал эту статью, очень смутно представлял, как работают lock-free алгоритмы, хотя сам порой использовал какую-нибудь джавовую ConcurrentLinkedQueue.

    А сейчас догнал, что они действительно lock-free, но не прям совсем так, чтобы не было блокировок — просто они не используются для блоков кода в принципе, алгоритм используя только атомарные переменные в цикле пытается применить некую операцию до тех пор, пока у него не получится (для добавления в очередь, например, это может быть просто CAS хвоста в цикле, если успеем заменить его до того, как успел кто-то другой — то и славно).

    Ну а wait-free — это характеристика lock-free алгоритма, при которой есть гарантии, что, например, для нашего примера очереди, мы не будем крутиться в цикле вечно (т.е. если все подряд кроме нас будут успевать поменять хвост, а нам постоянно будет не везти) — т.е. гарантии того, что есть какое-то конечное время (возможно зависящее от размера структуры данных/количества одновременных её пользователей), за которое мы успешно совершим некую операцию.
  • Конкурентность: Кооперативность
    0
    Совсем для новичков я ещё когда-то писал Code Hardcorius, но то было довольно давно и мне там уже много чего не нравится (про полиморфизм вообще дичь какая-то несуразная).
  • Почему земляне делают глючный софт и железо
    0

    Тип X предоставляет A если:


    1. Равен A.
    2. Является произведением типов, один из членов которого предоставляет A.
    3. Является суммой типов, все из членов которой предоставляют A.
    4. Возвращаемое значение функции предоставляет A.

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


    1. Ограничили типы `T = T → 0`
    2. Ограничили типы `T = (T, AnyOtherTypes) → 0`
    3. Ограничили типы `T = (T + (T, AnyOtherTypes)) → 0`
    4. Ограничили типы `T = (Unit → (T + (T, AnyOtherTypes))) → 0`

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


    A = B + C
    B = 1 + C
    C = 1 + B

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

  • Конкурентность: Параллелизм
    +1
  • Конкурентность: Параллелизм
    0
    1. Да, некорректно, просто не нашёл другого короткого понятия. Тут я имею в виду, что в случае с данной реализацией промисов всё сделано так, что можно сразу нескольким обработчикам дожидаться результата промиса. И метод .then там это примерно «если промис ещё резолвится, сделай addThenListener(callback), а если зарезолвен, то сделай callback(data)». Да, совсем не PubSub, чистые события.
    2. Промисы реализует кто как хочет, они действительно везде есть, но я хочу показать один из примеров, как их реализовывать не стоит (хотя некоторые плюсы всё же есть, о них тоже расскажу).
    3. Я расскажу о том, как работают обещания перед тем, как будет про async/await, всё будет ок.


    Удалил вообще из содержания пункт про Promises/A+.
  • Конкурентность: Параллелизм
    0
    Не упоминал про неё, ибо подумал, что она совсем уж микропараллельна, спасибо.

    Ещё про GPU ничего не сказал, хотя там параллельность так параллельность (в которых сотни ядер). Но оно специфично относительно мощных вычислений, а мне бы просто за событиями смотреть и байты туда-сюда копировать.
  • Мобильное приложение Signal обходит государственную блокировку, пропуская трафик через Google App Engine
    –1
    Сейчас Telegram очень интересный протокол связи использует (не менее интересно, что похоже он пока не задокументирован). При первом сообщении к серверу он генерирует 256-битный AES-ключ (32 байта), который записывает в заголовок первого пакета. И сразу же шифрует им же всё, кроме самого ключа.

    В итоге мы получаем сразу две плюшки:

    • Надёжнейшую контрольную сумму, если где-то поменялся бит — оно расшифруется в невалидную кашу
    • Невозможно определить протокол сервиса, ибо все наши данные это полнейший рандомный фарш
  • Конкурентность: Параллелизм
    –2
    Да, так и есть. Но ничто не мешает конкурентным процессам выполняться одновременно. Поэтому я всё-таки считаю, что параллелизм это одна из возможных оптимизаций конкурентных подходов, которые существуют вне зависимости от того, есть ли у нас возможность выполнять что-то параллельно на самом деле или нет.

    Абсолютно любой многопоточный алгоритм можно переписать в кооперативном стиле, когда код каждого потока будет делать явные yield'ы, чтобы передать управление другому потоку. Или выполнять код в виртуальной машине, считая операции (так, например, в Erlang'е работают их сверхлёгкие процессы).

    А когда у нас появляется многопоточность — наши подходы никак не меняются и в основном мы просто создаём столько потоков, сколько у нас логических ядер, и в них выполняем функции, которые берём с какой-нибудь очереди задач. В которую кладём обработчики разных событий, которые возникают от асинхронного I/O.

    (а синхронный I/O вряд ли уже кто использует, но его можно реализовать и без параллельного выполнения, достаточно в цикле проверять, есть ли данные, не блокируя, и передавать выполнение другому потоку, если данных нет)
  • Почему земляне делают глючный софт и железо
    +2
    Пока в промышленности формальная верификация активно используется только в hardware, где цена ошибки крайне высока. И там это необходимо.

    Apple, Intel (там ещё горстка других вакансий у них же), NXP, ARM.

    И софтварные вакансии тоже иногда появляются, я уверен. И нужно быть готовым к такой, ибо ты не скажешь «ребята, у вас крутая вакансия, подождите, я тут три года попрактикуюсь и сразу к вам».
  • Почему земляне делают глючный софт и железо
    0
    Не, у нас нет уникальных типов, есть вполне общий Массив n элементов типа T.

    И никто не мешает использовать эту n в рантайме.

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

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

    Буду дома, может накидаю какой пример кода, как именно оно будет проверяться.
  • Почему земляне делают глючный софт и железо
    +2
    Ещё немного уточню, почему именно так. Когда мы создаём функцию f, внутри неё у нас нет доступа к f и мы не можем сделать рекурсивный вызов. Можно двумя путями дать этот доступ — или передавать её в «конструктор функции», или использовать Y-комбинатор.

    Первый путь будет означать, что желая создать функцию f:(A → B), мы будем иметь F = (F → A → B).

    Мы только что создали рекурсивный тип. Рекурсивные типы полезны (к примеру, тип списка — рекурсивен: List T = Unit + (T, List T), но не все из них разрешены, т.к. существоние некоторых приводит к возможности получить элемент типа Ложь, в котором по определению нет элементов (т.е. получить противоречие в нашей теории, что уничтожает её целиком).

    На примере нашего F = (F → A → B): пусть A = Unit, B = 0 (0 = Ложь). Тогда f p a = p a. Мы корректно определили функцию f, которая валидна по типам, но, к сожалению, приняв значение типа Unit (которое у нас всегда в наличии по определению), мы получим противоречие.

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

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

    Мы просто разрешаем существовать только гарантированно выполнимым функциям. Проворачивается это каким-нибудь таким способом:

    recursive :
      A → B:(A → U) → C →
      order:TotallyOrdered C →
      HasLowerBound C order →
      IsDiscrete C order →
      extract:(A → C) →
      
      (a:A →
       (x:A → Lesser order (extract x) (extract a) → B x)
       → B a)
       
      → a:A → B a


    Мы хотим получить функцию A → B (принимающую значение типа A, возвращающее значение типа B), в которой хотим использовать рекурсию.

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

    И ещё просим функцию A → C, которая «оценит вес текущего значения A».

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

    И все наши программы будут завершаться когда-нибудь (если никто не будет вычислять что-нибудь невероятно долгое, вроде функций Аккермана — ограничивать по времени выполнения куда сложнее, придётся описывать виртуальную машину и ограничивать количество выполняемых инструкций).
  • Почему земляне делают глючный софт и железо
    +4
    Можно, один из них — теория типов.

    У вас сейчас статически типизированные программы типизированы не полностью. Т.е. у вас в наличии функции типа «принимает массив с элементами типа T и число, возвращает элемент типа T или создаёт исключение».

    А в более мощных системах типизации (где есть зависимые типы) можно иметь функции типа «принимает массив длины n с элементами типа T, число m, которое больше либо равно нуля, строго меньше n, возвращает элемент типа T».

    И тогда вам нужно будет передавать в эту функцию значения типа «m меньше n», чтобы ваша программа вообще смогла скомпилироваться.

    И так можно описывать абсолютно что угодно, включая то, как должны обрабатываться внешние события, хотя никто не верит.
  • Путеводитель по JavaScript Promise для новичков
    0
    Посмотрел, очень интересно, явное всегда лучше неявного. Спасибо большое, мне нравится этот подход, посмотрим как-нибудь.
  • Путеводитель по JavaScript Promise для новичков
    0
    Да, так и есть, нужно будет делать что-то вроде .then(checkedForCancel(function(data) { ... })), где checkedForCancel будет возвращать функцию, которая будет проверять какую-нибудь переменную, и только если она всё ещё хорошая, выполнять переданную.