openlang

Ручной перевод старого CoffeeScript как урок JavaScript

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

Почти у каждого кусочка сахара есть современный двойник, и в этом весь смысл, потому что двойники похожи, но не одинаковы. @name - это this.name. == всегда означал ===. a ? b выглядит как a ?? b и почти им является (только версия CoffeeScript принимает ещё и переменную, которую никогда не объявляли, и компилируется в проверку typeof, а ?? на необъявленном имени бросает ReferenceError, и как раз такие вещи ученик находит, только один раз ошибившись). Диапазоны, for own, неявные возвраты и soak-операторы отдают тем же привкусом «почти».

В итоге ученик читает спецификацию JavaScript ради семантики, а не синтаксиса, и мало какое упражнение для начинающих этого добивается.

Разговор продолжается 11

Следуя доводам
  1. Ссылка на ответ

    Чаще всего я бы ждала ошибку || вместо ??, потому что это более старая привычка и при беглом взгляде она проходит. Они расходятся на каждом ложном значении, которое не null и не undefined.

    const saved = 0
    console.log(saved || 10, saved ?? 10)
    console.log('' || 'none', '' ?? 'none')

    Это печатает 10 0, а затем none и пустую строку. ? в CoffeeScript считает 0 и '' присутствующими, так же как ??. Значит, перевод на || меняет поведение ровно на тех значениях, которые обычно лежат в экране настроек, и аннотация saved как number этого не заметит, ведь 0 - вполне себе number.

  2. Ссылка на ответ

    А потом значение приходит из поля ввода. Очищаю поле количества и жму «Сохранить». input.value равно '', а не null, так что ?? спокойно оставляет пустую строку, и на экране пусто там, где должно быть значение по умолчанию, а || возвращает 10 (и вернул бы 10 поверх настоящего нуля, который кто-то ввёл нарочно). Ни один из операторов не знает, что значит пустое поле. Это должен решить кто-то в коде, который читает поле, раньше, чем слово дадут любому из них.

  3. Ссылка на ответ

    Мне всегда нравилось слово, которое CoffeeScript выбрал для ?, existential, чуть больше, чем следовало бы. Вслух оно звучит так, будто спрашивает, существует ли вещь вообще, и для необъявленной переменной скомпилированная проверка typeof буквально это и спрашивает. Для объявленной она спрашивает только, не null ли значение и не undefined ли, и это куда более скромный вопрос в том же громком слове. JavaScript выбрал nullish coalescing, название неуклюжее, зато хотя бы говорит, о каких значениях речь.

  4. Ссылка на ответ

    В этом упражнении я бы поставил первым soak-вызов. callback?() в CoffeeScript компилируется с проверкой typeof callback === "function" перед вызовом, так что колбэк, в котором лежит строка или число, тихо пропускается. callback?.() в JavaScript проверяет только null и undefined. Передайте ему строку и получите TypeError, потому что строку нельзя вызвать, хотя строки отличаются всего одной точкой.

  5. Ссылка на ответ

    Я бы сделал помягче. Пусть первый черновик выдаст coffee -c, ученик объяснит каждую строку вывода, а потом приведёт её к тому JavaScript, который написал бы сам. Проверки typeof и собранные массивы он всё равно встретит, просто ответы уже на странице. Чистый ручной перевод звучит хорошо, но ученику, который неверно угадал про ?, ничто об этом не скажет, пока что-нибудь не сломается сильно позже.

  6. Ссылка на ответ

    Ответы на странице и есть проблема. Скомпилированный CoffeeScript - это представление CoffeeScript о JavaScript (временные ref и j, помощник hasProp на каждый for own, тернарник, проверяющий направление в каждом цикле по диапазону, всё совершенно правильно, и ничего не написано так, как пишет человек), и ученик, объясняя это построчно, в основном узнаёт, как думает компилятор. Любопытно, но урок не об этом. Полезная часть - как раз неверная догадка про ?. Лучше дать к упражнению маленький файл с тестами, и неверная догадка сразу проявится как упавший случай.

  7. Ссылка на ответ

    Сюрприз, по-моему, будет в диапазонах. Страница корзины рисует строку на каждый товар через for i in [1..count]. Очищаю корзину, count равно 0, и на странице появляются строки 1 и 0. Диапазоны CoffeeScript считают вниз, когда конец меньше начала, так что в [1..0] два элемента, а не ни одного. Ручной перевод будет for (let i = 1; i <= count; i++), и на нуле он ничего не делает, так что ученик исправляет ошибку, даже не заметив, что она была (лучший это исход упражнения или худший, честно, не могу решить).

  8. Ссылка на ответ

    У этого есть описанный в документации выход, by 1, он фиксирует направление (так что for i in [1..count] by 1 на нуле не выполняется ни разу), и про него легко было забыть, потому что счёт вниз проявляется только на том единственном входе, который никто не пробует первым. Но вот тихое исправление - это то, что я хотел бы услышать от ученика вслух. Если перевод ведёт себя не так, как оригинал, он должен суметь назвать вход, на котором это видно, и count = 0 для этого вполне годится.

  9. Ссылка на ответ

    Тут я на стороне corwin, по крайней мере насчёт циклов. Скомпилированный цикл по диапазону - худшее место, чтобы начинать что-либо объяснять. Проверка направления стоит в условии как 1 <= ref ? j <= ref : j >= ref, а потом ещё раз в приращении, так что ученик, разбирая это построчно, в основном учится читать вложенные тернарники, а настоящий вопрос, что будет на нуле, тонет под ними.

Другие стороны того же вопроса

  1. round(2.5) даёт 2, и ученики не верят

    Ученики упорно думают, что round(2.5) равно 3, а оба языка, к которым я обычно тянусь, говорят 2. Python даёт целое 2, Julia…

    Ответы 4juliapython
  2. 3 + 4 * 2 равно 14, и это ещё простая часть

    Каждый, кто начинает со Smalltalk, рано встречает 3 + 4 * 2 и слышит, что это 14, потому что бинарные сообщения идут слева на…

    Ответы 1smalltalk
  3. (x, x) не значит, что обе половины равны

    Ученики раз за разом читают (x, x) в OCaml как «обе половины равны». Это не так, и такой паттерн вообще недопустим. Новичок о…

    Ответы 5ocaml