Для вызывающего кода отсутствие аргумента для дефолтного параметра может также иметь семантику «я не хочу ничего решать, сделайте там сами что-нибудь разумное», в таком случае код не опирается на значения по умолчанию.
Более того, значения по умолчанию — это произвольные выражения, которые могут ссылаться на детали реализации библиотеки. «Осевшие» в вызывающем коде выражения дефолтных аргументов могли бы оказаться даже бинарно несоместимыми с новой версией библиотеки, поэтому их тоже можно считать деталями реализации.
под капотом в байт-коде генерируется каждая вариация метода с отсутствующими параметрами
На самом деле в этом месте в статье неточность, по умолчанию в байт-коде вообще не генерируются перегрузки без отдельных аргументов, а есть только два метода: один с настоящей сигнатурой функции (такой, как же, как в коде) и ещё один синтетический с добавленной битовой маской. Для вызовов из Котлина этого всегда хватает.
Если перегрузки без аргументов Вам всё-таки нужны (для Java-интеропа), то включить генерацию дополнительных методов можно с помощью аннотации @JvmOverloads. Но! Даже с ней перегрузок методов будет сгенерировано не 2^n (где n — число параметров со значениями по умолчанию), а всего n — это будут методы с отброшенными с конца параметрами.
В идиоматичном коде на Котлине часто встречаются и функции с большим числом дефолтных параметров. При сколько-нибудь существенном числе дефолтных параметров (даже уже на четырёх) генерировать 2^n перегрузок было бы очень накладно в смысле числа методов (актуально для Android) и размера класс-файлов. Для того, чтобы этого избежать, и нужен синтетический метод с битовой маской. С вызывающей стороны в битовой маске передаётся информация о том, для каких параметров аргументы переданы, а для каких должны быть использованы дефолтные. Синтетический метод проверяет битовую маску и на её основе вычисляет только нужные дефолтные значения, после чего передаёт всё вместе "настоящему методу".
То есть битовая маска нужна, чтобы не страдать от экспоненциального роста числа комбинаций отсутствующих аргументов.
Это нужно, чтобы значения по умолчанию не «оседали» в бинарниках, скомпилированных напротив библиотеки с функцией, у которой есть дефолтные значения. Такой подход позволяет заменить версию библиотеки и без перекомпиляции вызывающего кода получить её новые значения по умолчанию.
Тезис об универсальности математики в рассуждениях на такие темы меня всегда смущал. Я считаю, что наша математика проистекает из наших реалий. Например, гипотетическая форма жизни, развившаяся как один организм-колония на планете, где всё мелкодисперсное, звёзд не видно и нечего считать, вряд ли очевидным образом пришла бы к концепции натуральных чисел.
Спасибо за статью. По-моему, у вас получилось довольно хорошее объяснение нисходящего разбора. Ожидал увидеть в статье отсылки к парсер-комбинаторам, потому что их идея очень близка: там парсер — это что-то, что может "откусить" префикс строки и выдать результат и оставшуюся строку, но их ещё и комбинируют, чтобы из простых парсеров (база — один токен) получать более сложные (вплоть до парсера всего языка).
Одно из самых полезных применений моноида — в дереве отрезков, которое для отрезка [1, n] хранит в узлах результаты операции только на некоторых подотрезках (длины 2^k, не пересекающихся друг с другом на каждом значении длины), а за счёт свойств моноида результат на произвольном подотрезке можно получить за O(log n), потому что любой подотрезок оказывается полностью покрыт <= 4 сохранёнными в дереве подотрезками, и нужно только найти в дереве эти результаты и выполнить над ними операцию. Ну, и обновление элемента тоже за O(log n), потому что он входит в такое количество сохранённых в дереве подотрезков.
Почему же? Насколько я понял, доказательство в статье как раз конструктивное: приводится контрпример, задача из NP, для которой нет полиномиального алгоритма.
Позвольте добавить про инстинкт самосохранения: всё-таки именно в формулировке инстинкта его, похоже, нет. Скорее всего, есть в виде усвоенного поведения.
Вы же, наверное, имели в виду генерацию Java-файлов annotation processor'ом из аннотаций в коде на Kotlin? Обычные исходники на Kotlin компилируются напрямую в байт-код, без трансляции в Java.
<...> если мы используем переданный в конструктор параметр, то он будет присвоен полю сразу, минуя переопределенный setter
Тут можно сделать что-нибудь в духе
class User(nameParam: String) {
var name: String = ""
set(value) { field = value.toUpperCase() }
init { name = nameParam } // вызовется сеттер
}
Лямбда, вложенная в лямбду
Про такое, кстати, даже в документации (coding conventions) сказано, что если лямбды вложены, то у всех лучше явно писать все параметры вместо использования it.
Форматтеру IDEA вполне можно запретить форматировать участок кода, но по умолчанию эта фича выключена. Например, здесь это позволило избежать лишнего большого отступа, который бы добавился из-за того, что это аргумент конструктора. :)
Согласен, во время написания поста тоже думал, что для цепочек вызовов это должно быть осуществимо примерно так, как написано у вас (спасибо что показали, как именно!). В таком случае остаётся только такой недостаток, как необходимость воевать с форматтером, чтобы он не выровнял все вызовы функций.
Более того, значения по умолчанию — это произвольные выражения, которые могут ссылаться на детали реализации библиотеки. «Осевшие» в вызывающем коде выражения дефолтных аргументов могли бы оказаться даже бинарно несоместимыми с новой версией библиотеки, поэтому их тоже можно считать деталями реализации.
На самом деле в этом месте в статье неточность, по умолчанию в байт-коде вообще не генерируются перегрузки без отдельных аргументов, а есть только два метода: один с настоящей сигнатурой функции (такой, как же, как в коде) и ещё один синтетический с добавленной битовой маской. Для вызовов из Котлина этого всегда хватает.
Если перегрузки без аргументов Вам всё-таки нужны (для Java-интеропа), то включить генерацию дополнительных методов можно с помощью аннотации
@JvmOverloads
. Но! Даже с ней перегрузок методов будет сгенерировано не 2^n (где n — число параметров со значениями по умолчанию), а всего n — это будут методы с отброшенными с конца параметрами.В идиоматичном коде на Котлине часто встречаются и функции с большим числом дефолтных параметров. При сколько-нибудь существенном числе дефолтных параметров (даже уже на четырёх) генерировать 2^n перегрузок было бы очень накладно в смысле числа методов (актуально для Android) и размера класс-файлов. Для того, чтобы этого избежать, и нужен синтетический метод с битовой маской. С вызывающей стороны в битовой маске передаётся информация о том, для каких параметров аргументы переданы, а для каких должны быть использованы дефолтные. Синтетический метод проверяет битовую маску и на её основе вычисляет только нужные дефолтные значения, после чего передаёт всё вместе "настоящему методу".
То есть битовая маска нужна, чтобы не страдать от экспоненциального роста числа комбинаций отсутствующих аргументов.
Тезис об универсальности математики в рассуждениях на такие темы меня всегда смущал. Я считаю, что наша математика проистекает из наших реалий. Например, гипотетическая форма жизни, развившаяся как один организм-колония на планете, где всё мелкодисперсное, звёзд не видно и нечего считать, вряд ли очевидным образом пришла бы к концепции натуральных чисел.
Спасибо за статью. По-моему, у вас получилось довольно хорошее объяснение нисходящего разбора. Ожидал увидеть в статье отсылки к парсер-комбинаторам, потому что их идея очень близка: там парсер — это что-то, что может "откусить" префикс строки и выдать результат и оставшуюся строку, но их ещё и комбинируют, чтобы из простых парсеров (база — один токен) получать более сложные (вплоть до парсера всего языка).
МТС, Питер, подтверждаю: не работают Play, Drive, YouTube, частично (один из аккаунтов) Inbox, Remote Desktop. Усё пропало, шеф!
Если это даже 4% их доходов, то вот лично я вполне согласен на увеличение цен в 1 / 0.96 ~ 1.042 раза при условии отмены роуминга.
А считается ли заражением компьютера через ДНК допуск к нему криворукого пользователя?
Вы же, наверное, имели в виду генерацию Java-файлов annotation processor'ом из аннотаций в коде на Kotlin? Обычные исходники на Kotlin компилируются напрямую в байт-код, без трансляции в Java.
Тут можно сделать что-нибудь в духе
Про такое, кстати, даже в документации (coding conventions) сказано, что если лямбды вложены, то у всех лучше явно писать все параметры вместо использования
it
.