Наткнулся тут на одну статью, которая открыла мне глаза на все то, о чем я думал последнее время и чего избегал.
Часто меня спрашивали: почему мне не хочется путешествовать? На что шуточно отвечал, что насмотрелся Птушкина, а теперь уже нечего смотреть, что там за бугром. В эту шутку я на самом деле вкладывал настоящие ощущения и переживания. Мне действительно казалось, что ничего интересного уже нету, и все я уже видел. А это оказался самый большой обман нашего мозга, что воображаемое он воспринимает как действительное.
Вообще мозг воспринимает как действительное не только позитивные вещи по типу роликов на YouTube, но и негативные. То есть любые размышления о чем плохом влияют на нас так же, как если бы мы по настоящему переживали этот момент. А самое интересное, что размышления о мечте имеют такой же эффект.
Я много раз слышал совет о том, что не нужно часто и детально говорить про свою мечту. Якобы если скажешь, то не добьёшься его. В этом есть смысл, потому что рассказывая про свою мечту, мы уже ощущаем себя так, будто достигли её. Это может очень сильно помешать, а если все таки не помешает, то ощущения от достижения будут абсолютно никакие.
Например, если человек мечтает получить повышения на работе. Делает для этого все, но если постоянно думает об этом, примеряет на себе роль, например, руководителя, то, став этим руководителем, он просто ощутит не достижение этой мечты, а данность этого. Ведь так и должно было быть и никак по другому, а может даже опоздали с повышением, потому что человек уже давно вошёл в роль.
Другой пример с фанатиками. Человек, который начинает в чем-то сильно разбираться, например, пересмотрев тучу роликов про кунг-фу, начинает ощущать себя мастером в этом, как будто он по настоящему владеет боевыми искусствами.
Суть размышлений сводится, что стоит ограничить потребление контента о вещах, которые нравятся. Чаще всего это просто готовый результат, который хотел наш мозг и он получил его без всякого напряга. (Скорее всего из-за того, что мир настолько быстро развивается, но развитие нашего организма отстаёт, но это отдельная история). И в таких условиях нужно стараться получать свой уникальный опыт, не зацикливаясь на своей мечте, тем самым избавляя себя от рутинности и увеличивая шансы по достижению к цели.
Поэтому:
- YouTube может быть вреден и может красть мечты. Даже если вы не любитель помечтать
- мечтать иногда вредно и нужно держать себя в руках и не впадать в долгие размышления.
https://telegra.ph/Problema-mechtanij-07-12
Часто меня спрашивали: почему мне не хочется путешествовать? На что шуточно отвечал, что насмотрелся Птушкина, а теперь уже нечего смотреть, что там за бугром. В эту шутку я на самом деле вкладывал настоящие ощущения и переживания. Мне действительно казалось, что ничего интересного уже нету, и все я уже видел. А это оказался самый большой обман нашего мозга, что воображаемое он воспринимает как действительное.
Вообще мозг воспринимает как действительное не только позитивные вещи по типу роликов на YouTube, но и негативные. То есть любые размышления о чем плохом влияют на нас так же, как если бы мы по настоящему переживали этот момент. А самое интересное, что размышления о мечте имеют такой же эффект.
Я много раз слышал совет о том, что не нужно часто и детально говорить про свою мечту. Якобы если скажешь, то не добьёшься его. В этом есть смысл, потому что рассказывая про свою мечту, мы уже ощущаем себя так, будто достигли её. Это может очень сильно помешать, а если все таки не помешает, то ощущения от достижения будут абсолютно никакие.
Например, если человек мечтает получить повышения на работе. Делает для этого все, но если постоянно думает об этом, примеряет на себе роль, например, руководителя, то, став этим руководителем, он просто ощутит не достижение этой мечты, а данность этого. Ведь так и должно было быть и никак по другому, а может даже опоздали с повышением, потому что человек уже давно вошёл в роль.
Другой пример с фанатиками. Человек, который начинает в чем-то сильно разбираться, например, пересмотрев тучу роликов про кунг-фу, начинает ощущать себя мастером в этом, как будто он по настоящему владеет боевыми искусствами.
Суть размышлений сводится, что стоит ограничить потребление контента о вещах, которые нравятся. Чаще всего это просто готовый результат, который хотел наш мозг и он получил его без всякого напряга. (Скорее всего из-за того, что мир настолько быстро развивается, но развитие нашего организма отстаёт, но это отдельная история). И в таких условиях нужно стараться получать свой уникальный опыт, не зацикливаясь на своей мечте, тем самым избавляя себя от рутинности и увеличивая шансы по достижению к цели.
Поэтому:
- YouTube может быть вреден и может красть мечты. Даже если вы не любитель помечтать
- мечтать иногда вредно и нужно держать себя в руках и не впадать в долгие размышления.
https://telegra.ph/Problema-mechtanij-07-12
👍3
Посмотрел недавно видео у ЧД, но показалось, что вывод в итоге неправильный.
Я думаю, что действительно выбор исходит из опыта жизненного, и этот опыт приходит от кого-то: от родителей, друзей, знакомых и тд. То есть мы друг на друга влияем и определяем будущее друг друга и тот опыт, который мы будем получать. Соответственно, ЧД в видео допускает, что есть свобода выбора, но я считаю, что его нет и нами управляет опыт, который мы получаем.
Представим, что мозг - это сознательное и несознательное. Наше сознательное зависит от несознательного. Несознательное решает, что мы будем говорить, делать. Оно управляет всем нашим телом и процессами, а сознательное - это лишь наша оболочка, то как мы себя воспринимаем. О нашем сознательном заботится и переживает наше несознательное внутренними процессами, изменением настроения с помощью гармонов и тд.
Несознательное действует на основе полученного опыта и даёт команду сознательному, будто оно само решило. Так что мы просто наблюдаем за несознательным в нашей голове на рельсах предопределения, направление которого меняет лишь опыт или знание.
Действия предопределяются, но обычно они коллерилируют с жизненным опытом.
Этот жизненный опыт набирается. И любое знание и опыт должно войти в бессознательное, которое необходимо понять и принять, чтобы оно определяло твоё сознательное и твоё представление для этого мира и для себя самого.
В итоге, я думаю предопределение есть. Выбор так же предопределен, но его предопределение зависит от того, какой опыт ты получаешь и в какой среде оказываешься.
https://youtu.be/LGjRODjpDGg
Я думаю, что действительно выбор исходит из опыта жизненного, и этот опыт приходит от кого-то: от родителей, друзей, знакомых и тд. То есть мы друг на друга влияем и определяем будущее друг друга и тот опыт, который мы будем получать. Соответственно, ЧД в видео допускает, что есть свобода выбора, но я считаю, что его нет и нами управляет опыт, который мы получаем.
Представим, что мозг - это сознательное и несознательное. Наше сознательное зависит от несознательного. Несознательное решает, что мы будем говорить, делать. Оно управляет всем нашим телом и процессами, а сознательное - это лишь наша оболочка, то как мы себя воспринимаем. О нашем сознательном заботится и переживает наше несознательное внутренними процессами, изменением настроения с помощью гармонов и тд.
Несознательное действует на основе полученного опыта и даёт команду сознательному, будто оно само решило. Так что мы просто наблюдаем за несознательным в нашей голове на рельсах предопределения, направление которого меняет лишь опыт или знание.
Действия предопределяются, но обычно они коллерилируют с жизненным опытом.
Этот жизненный опыт набирается. И любое знание и опыт должно войти в бессознательное, которое необходимо понять и принять, чтобы оно определяло твоё сознательное и твоё представление для этого мира и для себя самого.
В итоге, я думаю предопределение есть. Выбор так же предопределен, но его предопределение зависит от того, какой опыт ты получаешь и в какой среде оказываешься.
https://youtu.be/LGjRODjpDGg
🔥2🤔1
Недавно ходил на одну встречу сообщества по мобильной разработке.
На этой встрече обсуждали статью о чистой архитектуре. Во время разговора постоянно слышал о слоях, что их всего три, а MVx паттерны - это паттерны презентационного слоя.
Тут я не удержался и завязал спор и очень долгий. Я против всего окружения. Мне казалось, что ребята просто мыслят шаблонами, а им казалось, наверное, что я новичок и не постиг их дзена.
О чем спор?
С их стороны основные доводы были следующие:
- MVx это паттерны presentation слоя
- слоев в Clean Architecture всего три: presentation, domain и data. Даже если в них нету кода(что?)
- проект всегда должен состоять из этих трех слоев и всегда нужно думать об этих трех слоях при разработке проекта
С моей стороны:
- архитектурные паттерны такие как MVx и шаблоны существуют вне рамок. Это самая не важная деталь проекта.
- слоев может быть сколько угодно, а может и не быть вовсе.
- все архитектурные паттерны, шаблоны и слои - это шаблоны, которые пришли к нам с развитием индустрии и являются только лишь рекомендацией, а не правилом
- при проектировании архитектуры важно продумывать все на уровне компонентов и тогда все ляжет на любой шаблон.
Постараюсь объяснить свои доводы подробнее.
Если взять книгу, которая является источников всех ошибочных рассуждений, от Роберта Мартина и прочитать её, то можно заметить, что по большой части в книги описывается взаимодействие компонентов. Основное, что я понял из этой книги и своей практики это то, что нужно:
- уметь определять стабильные и не стабильные компоненты, то есть что будет или не будут изменяться
- уметь балансировать между количеством стабильных и абстрактных компонентов
- защищать одни компоненты от других путем инверсии контроля
- определять как компоненты будут друг с другом общаться, что должны друг о друге знать и каким образом должны использоваться
- уметь находить семантические и технические связи между компонентами
- стараться делать компоненты более сплоченными, но менее связанными
- и т. д.
Таких рекомендации достаточно много, но суть в том, что при проектировании архитектуры, нужно проектировать взаимодействие компонентов. В таком случае архитектура ляжет на любой шаблон, а любой паттерн возникнет сам по себе. А вот если пытаться изначально думать в рамках шаблонов, то чаще всего все превращается в кашу и компании страдают от того, что долго переезжают с одного MVx паттерна на другой.
Архитектура каждого проекта по своему уникальна и её нужно продумывать, а не пытаться распилить на слои. Что мне собственно и пытались доказать: что моя архитектура, даже если я не предполагал её разделение на слои, очень хорошо делится на слои. Оно делится хорошо, потому что продумано взаимодействие компонентов и только.
Откуда вообще появились слои и почему их три? Как мне кажется это туча статей, которые перефразировали содержание книги и которые как пример приводили картинку, где Роберт Мартин нарисовал линии. Почему их три? Потому что так проще воспринимается. И все. Но не нужно забывать, что в мире туча архитектур и архитекторов. Если бы все было так просто как с тремя слоями...
В защиту слоев могу сказать, что такой шаблон показывает себя хорошо в шаблонных проектах: получить с бекенда, распарсить, показать на экране. И младших разработчиков действительно проще научить думать в рамках этих слоев, но вот проще ли понимать? Мне кажется, что нет. Я так по молодости рапилил одну фичу по идеологиям трех слоев. Получилось, но все стало расплывчатое, а связи искать стало сложнее. Поэтому правильная архитектура должна решать конкретные проблемы и обязательно описывать то, как она их решает. А разработчику нужно в ней разобраться. Ошибочно считать, что он разберётся быстрее, если это шаблон.
Последнее, что хочется сказать по поводу шаблонов, что некоторые ограничения на самом деле хорошая вещь и открывают возможности для творчества. Но если ограничения навязаны и слишком шаблонные, то скорее всего они будут только мешать.
На этой встрече обсуждали статью о чистой архитектуре. Во время разговора постоянно слышал о слоях, что их всего три, а MVx паттерны - это паттерны презентационного слоя.
Тут я не удержался и завязал спор и очень долгий. Я против всего окружения. Мне казалось, что ребята просто мыслят шаблонами, а им казалось, наверное, что я новичок и не постиг их дзена.
О чем спор?
С их стороны основные доводы были следующие:
- MVx это паттерны presentation слоя
- слоев в Clean Architecture всего три: presentation, domain и data. Даже если в них нету кода(что?)
- проект всегда должен состоять из этих трех слоев и всегда нужно думать об этих трех слоях при разработке проекта
С моей стороны:
- архитектурные паттерны такие как MVx и шаблоны существуют вне рамок. Это самая не важная деталь проекта.
- слоев может быть сколько угодно, а может и не быть вовсе.
- все архитектурные паттерны, шаблоны и слои - это шаблоны, которые пришли к нам с развитием индустрии и являются только лишь рекомендацией, а не правилом
- при проектировании архитектуры важно продумывать все на уровне компонентов и тогда все ляжет на любой шаблон.
Постараюсь объяснить свои доводы подробнее.
Если взять книгу, которая является источников всех ошибочных рассуждений, от Роберта Мартина и прочитать её, то можно заметить, что по большой части в книги описывается взаимодействие компонентов. Основное, что я понял из этой книги и своей практики это то, что нужно:
- уметь определять стабильные и не стабильные компоненты, то есть что будет или не будут изменяться
- уметь балансировать между количеством стабильных и абстрактных компонентов
- защищать одни компоненты от других путем инверсии контроля
- определять как компоненты будут друг с другом общаться, что должны друг о друге знать и каким образом должны использоваться
- уметь находить семантические и технические связи между компонентами
- стараться делать компоненты более сплоченными, но менее связанными
- и т. д.
Таких рекомендации достаточно много, но суть в том, что при проектировании архитектуры, нужно проектировать взаимодействие компонентов. В таком случае архитектура ляжет на любой шаблон, а любой паттерн возникнет сам по себе. А вот если пытаться изначально думать в рамках шаблонов, то чаще всего все превращается в кашу и компании страдают от того, что долго переезжают с одного MVx паттерна на другой.
Архитектура каждого проекта по своему уникальна и её нужно продумывать, а не пытаться распилить на слои. Что мне собственно и пытались доказать: что моя архитектура, даже если я не предполагал её разделение на слои, очень хорошо делится на слои. Оно делится хорошо, потому что продумано взаимодействие компонентов и только.
Откуда вообще появились слои и почему их три? Как мне кажется это туча статей, которые перефразировали содержание книги и которые как пример приводили картинку, где Роберт Мартин нарисовал линии. Почему их три? Потому что так проще воспринимается. И все. Но не нужно забывать, что в мире туча архитектур и архитекторов. Если бы все было так просто как с тремя слоями...
В защиту слоев могу сказать, что такой шаблон показывает себя хорошо в шаблонных проектах: получить с бекенда, распарсить, показать на экране. И младших разработчиков действительно проще научить думать в рамках этих слоев, но вот проще ли понимать? Мне кажется, что нет. Я так по молодости рапилил одну фичу по идеологиям трех слоев. Получилось, но все стало расплывчатое, а связи искать стало сложнее. Поэтому правильная архитектура должна решать конкретные проблемы и обязательно описывать то, как она их решает. А разработчику нужно в ней разобраться. Ошибочно считать, что он разберётся быстрее, если это шаблон.
Последнее, что хочется сказать по поводу шаблонов, что некоторые ограничения на самом деле хорошая вещь и открывают возможности для творчества. Но если ограничения навязаны и слишком шаблонные, то скорее всего они будут только мешать.
Стоит ещё сказать, что даже у подхода со сплоченностью и связанностью есть альтернативы. Одна из них описана в блоге:
https://edmundkirwan.com/general/c-and-c.html
https://edmundkirwan.com/general/cdd.html
Автор в своих статьях преследует идею того, что основная проблема архитектуры ПО - это волновой эффект, который может задеть много классов при изменениях. В качестве решения предлагает проектировать неглубокую систему и очень и очень много времени уделять инкапсуляции. Показалось, что автор евангелист этих подходов и у него множество статей на эти две темы - причём очень не плохих.
В итоге, приходим к тому, что архитектура - это вечная тема и изучают её довольно умные люди и они же предлагают разные подходы, поэтому было бы неправильно ограничивать себя шаблонами и каждыть раз наступать на одни и те же грабли, а стоило бы учитывать большое количество подходов и создавать архитектуру под конкретные цели с планами на будущее.
https://edmundkirwan.com/general/c-and-c.html
https://edmundkirwan.com/general/cdd.html
Автор в своих статьях преследует идею того, что основная проблема архитектуры ПО - это волновой эффект, который может задеть много классов при изменениях. В качестве решения предлагает проектировать неглубокую систему и очень и очень много времени уделять инкапсуляции. Показалось, что автор евангелист этих подходов и у него множество статей на эти две темы - причём очень не плохих.
В итоге, приходим к тому, что архитектура - это вечная тема и изучают её довольно умные люди и они же предлагают разные подходы, поэтому было бы неправильно ограничивать себя шаблонами и каждыть раз наступать на одни и те же грабли, а стоило бы учитывать большое количество подходов и создавать архитектуру под конкретные цели с планами на будущее.
👍1
Matrix в Android вводит в заблуждение.
В школе и в ВУЗе, когда сталкивался с векторами и матрицами не понимал зачем они могут мне понадобиться в будущем. Векторы вряд-ли буду использовать, а для преобразований всегда есть обертки в виде Matrix или View сразу можно выполнить нужную трансформацию.
Шло время задач с векторами и матрицами становилось все больше) С векторами пришлось разбираться глубже при реализации функционала рисования или определения направления движения пальца пользователя, а вот матрицы... где только я их не начал использовать. Но однажды я столкнулся с проблемой.
Проблема была в том, что после множества трансформации мне понадобилось считать эти трансформации, но по отдельности.
Матрица в Android хранит в себе данные по масштабированию, наклону, перемещению и перспективе и предоставляет индексы, по которым можно получить информацию о конкретной трансформации.
В Википедии есть статья по матрице поворота, по которому можно сделать вывод, что:
В кратце, узнали угол и благодаря этому вычислили
Все хорошо до тех пор, пока мы не применим skew операции к матрице, тогда опять все едет. И это на самом деле правильно, ведь в наших выражениях есть угол и масштабирование, но нету наклона.
Благо за нас уже все посчитали и все гуглится как
В итоге получается, что:
Повопрот можно выразить так:
https://youtu.be/FmlurQExkxg
https://youtu.be/ELpwXUe46dQ
https://youtu.be/RqZH-7hlI48
Но по настоящему углубиться в тему позволят эти ресурсы, где описаны аффинные преобразования плоскости. На хабре нашлась неплохая базовая статья по ним:
https://habr.com/ru/articles/539420/
https://en.wikipedia.org/wiki/Affine_transformation
В школе и в ВУЗе, когда сталкивался с векторами и матрицами не понимал зачем они могут мне понадобиться в будущем. Векторы вряд-ли буду использовать, а для преобразований всегда есть обертки в виде Matrix или View сразу можно выполнить нужную трансформацию.
Шло время задач с векторами и матрицами становилось все больше) С векторами пришлось разбираться глубже при реализации функционала рисования или определения направления движения пальца пользователя, а вот матрицы... где только я их не начал использовать. Но однажды я столкнулся с проблемой.
Проблема была в том, что после множества трансформации мне понадобилось считать эти трансформации, но по отдельности.
Матрица в Android хранит в себе данные по масштабированию, наклону, перемещению и перспективе и предоставляет индексы, по которым можно получить информацию о конкретной трансформации.
MSCALE_X = 0
MSKEW_X = 1
MTRANS_X = 2
MSKEW_Y = 3
MSCALE_Y = 4
MTRANS_Y = 5
MPERSP_0 = 6
MPERSP_1 = 7
MPERSP_2 = 8
Но чего-то не хватает... Не хватает поворота, хотя API матрицы позволяет это делать через postRotate, setRotate или preRotate. Вот начинается интересное. В Википедии есть статья по матрице поворота, по которому можно сделать вывод, что:
MSCALE_X == cosA
MSKEW_X == -sinA
MSKEW_Y == sinA
MSCALE_Y == cosA
Тогда получается, что чтобы получить угол поворота, нужно передать значение под, например, MSCALE_X обратной тригонометрической функции. В данном случае выглядеть это будет так: acos(matrixValues[MSCALE_X]) * 180 / PI
Но стоит нам кроме поворота применить еще и масштабирования, и наш acos начинает возвращать совсем не тот результат. Все дело в том, что на самом деле значения по индексам выглядят так:MSCALE_X == cosA * scaleX
MSKEW_X == -sinA * scaleY
MSKEW_Y == sinA * scaleX
MSCALE_Y == cosA * scaleY
И чтобы получить scale, например, по x нужно:(sinA * scaleX) / (cosA * scaleY) = sinA / cosA = tanA(cosA * scaleX) / cos(atan(tanA))В кратце, узнали угол и благодаря этому вычислили
cosA, потом разделели исходное выражение и нашли scaleX.Все хорошо до тех пор, пока мы не применим skew операции к матрице, тогда опять все едет. И это на самом деле правильно, ведь в наших выражениях есть угол и масштабирование, но нету наклона.
Благо за нас уже все посчитали и все гуглится как
Decompose Transformation Matrix
https://drafts.csswg.org/css-transforms/#decomposing-a-2d-matrixВ итоге получается, что:
Повопрот можно выразить так:
atan2(matrixValues[MSKEW_X], matrixValues[MSCALE_X]) * (180 / PI)
А масшатибрование получить следующим образом:scaleX = hypot(matrixValues[MSCALE_X], matrixValues[MSKEW_Y])
scaleY = hypot(matrixValues[MSCALE_Y], matrixValues[MSKEW_X])
Понять почему так мне помогли следующие ресурсы:https://youtu.be/FmlurQExkxg
https://youtu.be/ELpwXUe46dQ
https://youtu.be/RqZH-7hlI48
Но по настоящему углубиться в тему позволят эти ресурсы, где описаны аффинные преобразования плоскости. На хабре нашлась неплохая базовая статья по ним:
https://habr.com/ru/articles/539420/
https://en.wikipedia.org/wiki/Affine_transformation
👍1
Сегодня потратил полдня на то, чтобы подцепить debugger к компилятору Kotlin.
Делал это давно уже, а тут сходу не получилось. Поэтому создавал новый проект, переписывал многое, а проблемы решал с помощью dump в IR, то есть в принципе обходился и без дебагера, но, как мне кажется, если бы я смог его завести чуть раньше, то и задачи бы сделал быстрее.
А насчёт такого изменения, что -D флаг поменяли на -P - это очень неожиданно. Предполагается, что -D - это больше Gradle based флаги, которые влияют на процесс сборки. Логически флаги Kotlin туда не выписывались. Например, другие флаги компиляции Kotlin являются проектными, то есть передаются через -P. Но почему то этот флаг был как исключение -D. Я думаю, что изначально подумали, что раз этот флаг влияет на способ сборки, то сделаем его -D, а потом тихо передумали.
Делал это давно уже, а тут сходу не получилось. Поэтому создавал новый проект, переписывал многое, а проблемы решал с помощью dump в IR, то есть в принципе обходился и без дебагера, но, как мне кажется, если бы я смог его завести чуть раньше, то и задачи бы сделал быстрее.
А насчёт такого изменения, что -D флаг поменяли на -P - это очень неожиданно. Предполагается, что -D - это больше Gradle based флаги, которые влияют на процесс сборки. Логически флаги Kotlin туда не выписывались. Например, другие флаги компиляции Kotlin являются проектными, то есть передаются через -P. Но почему то этот флаг был как исключение -D. Я думаю, что изначально подумали, что раз этот флаг влияет на способ сборки, то сделаем его -D, а потом тихо передумали.
🌚1
Самое страшное (или я пока не привык) в разработке компиляторных плагинов это ошибки в codegen части, которая, например, для JVM генерирует байт-код.
Написал плагин, протестировал - все ок. Создают MR - получаю краш на CI. Что же не так?
Расскажу какая задача была: нужно было найти цепочку вызовов функции и между ними вставить кастомную. Звучит и делается несложно. Главное, что нужно было сделать, определить правильно reciever функции, которая сдвигается вниз цепочки после вставки дополнительной.
Что за reciever? Можно понимать это как класс, на котором вызывается функция. Их три штуки на моей памяти: context, dispatch, extension.
context - это новая фича в Kotlin, но еще экспериментальная. Идея в том, чтобы у функции были ограничения, где ее можно вызывать. Разруливается эта штука еще на frontend этапе и просто выдает ошибку, если вызов функции не сходится с требованиями - это аналогично тому, если бы мы забыли передать параметр и idea нам это подстветила, а мы все равно попытались собрать - получили бы краш на уровне frontend, ибо зачем собирать то, что неправильно написано. В backend части поэтому насчет context receiver не нужно беспокоиться.
dispatch - reciever для прямого вызова функции который определен внутри класса/интерфейса, экземпляром которого и является reciever
extension - да, это про extension функции. У них свой тип reciever
Так вот, чтобы внедриться в цепочку я определял какой тип reciever стоит у функции, которую сдвигаю, и устанавливал в один из dispatch и reciever и удалял в другой.
Ошибка была в том, что есть функции, которые имеют сразу оба reciever. Как?
Например:
Обычно такой инструкцией имитируют context reciever и думаю это будет продолжаться(сам так делал), пока он не выйдет из экспериментального состояния.
Получается, что extension функция внутри класса дополнительный overhead? Может быть, но это стоит проверить. Насколько можно сэкономить на времени компиляции, если освободить компилятор он построения дополнительных связей.
Вопрос похож на тот, каким задавились ребята из Badoo, когда выпиливали все функции из data class'а, но они мерили размер приложения. Но в этой ситуации думаю на размер никакого влияния не будет.
Под конец ссылка на все параметры компилятора Kotlin, чтобы очередной флаг не казался магией)
Написал плагин, протестировал - все ок. Создают MR - получаю краш на CI. Что же не так?
Расскажу какая задача была: нужно было найти цепочку вызовов функции и между ними вставить кастомную. Звучит и делается несложно. Главное, что нужно было сделать, определить правильно reciever функции, которая сдвигается вниз цепочки после вставки дополнительной.
Что за reciever? Можно понимать это как класс, на котором вызывается функция. Их три штуки на моей памяти: context, dispatch, extension.
context - это новая фича в Kotlin, но еще экспериментальная. Идея в том, чтобы у функции были ограничения, где ее можно вызывать. Разруливается эта штука еще на frontend этапе и просто выдает ошибку, если вызов функции не сходится с требованиями - это аналогично тому, если бы мы забыли передать параметр и idea нам это подстветила, а мы все равно попытались собрать - получили бы краш на уровне frontend, ибо зачем собирать то, что неправильно написано. В backend части поэтому насчет context receiver не нужно беспокоиться.
dispatch - reciever для прямого вызова функции который определен внутри класса/интерфейса, экземпляром которого и является reciever
extension - да, это про extension функции. У них свой тип reciever
Так вот, чтобы внедриться в цепочку я определял какой тип reciever стоит у функции, которую сдвигаю, и устанавливал в один из dispatch и reciever и удалял в другой.
Ошибка была в том, что есть функции, которые имеют сразу оба reciever. Как?
Например:
interface RowScope {
fun Modifier.align()
}
extension функция внутри класса. Именно в таком случае нужны оба типа reciever: extension и dispatch. В extension будет Modifier, а dispatch - экземпляр RowScope.Обычно такой инструкцией имитируют context reciever и думаю это будет продолжаться(сам так делал), пока он не выйдет из экспериментального состояния.
Получается, что extension функция внутри класса дополнительный overhead? Может быть, но это стоит проверить. Насколько можно сэкономить на времени компиляции, если освободить компилятор он построения дополнительных связей.
Вопрос похож на тот, каким задавились ребята из Badoo, когда выпиливали все функции из data class'а, но они мерили размер приложения. Но в этой ситуации думаю на размер никакого влияния не будет.
Под конец ссылка на все параметры компилятора Kotlin, чтобы очередной флаг не казался магией)
👍1
Боли поддержки IDEA плагинов.
Одним вечером допиливал корпоративный плагин, добавляя новые инструменты для работы с Compose. По ходу дела решил поднять версии kotlin, gradle и intellij plugin. Версии выбрал больше от балды, чтобы был доступен новый API. В итоге все сделал, проверил, пересобрал jar-ники, опубликовал и пошел спать.
Через пару дней прилетает жалоба, что плагин крашится при попытке запустить основную, но очень старую функциональность. Думал, что может я что-то сломал, случайно. Или старое апи выпили, которое мы использовали (как это обычно бывает), но я же собрал, протестировал все. Все было гуд, а теперь крашится даже у меня.
Ошибка такая:
Переписал код, чтобы избавиться от вложенный
В сгенерированном java коде описано содержимое формы, где название поля - это id ui компонента в форме. Выглядит это так:
Возвращаясь к проблеме. Почему resources null, вроде все работало как часы, да и я в дебаге все тестил обычно. Запускаю снова в дебаг режиме плагин...и он работает, все инициализировано. Собираю jar-ник, устанавливаю и опять получаю ошибку.
Начинаю исследовать jar для дебага и публикации (отличаются таски для сборки: instrumentJar и просто jar) и эврика - просто нету кода, который собирает ui по форме) (рис. 1)
Тут можно было бы остановиться, запустить таску instrumentJar, собрать новые артефакты, опубликовать и пойти дальше, но хочется понять почему сломалось и возможно найти лучшее решение.
Так как поднималась версия intellij gradle плагина, который и отвечает за генерацию этого кода. Потыкал разные версии и обнаружил, что сломалась генерация с 1.12.0 по 1.13.0. Ну как сломалась? Просто вдруг в версии 1.13.0 начали выпиливать генерируемый код для форм в jar таске, а в instrumentJar оставили, поэтому у меня в дебаг версии все работало.
Выходит, что теперь никак не собрать jar таской плагин с генерируемым кодом. Мне стало интересно, как Jetbrains обошли это в своих семплах - там точно были примеры использования form. Но они там все уже выпили и вручную собирают ui.
Неприятно, но есть несколько вариантов решить проблему:
1. Сделать как в семплах, то есть отказаться от форм и все переписать.
2. Генерировать артефакты таской instrumentJar
3. Сгенерировать код для форм и добавить в исходный код формы.
Выбрал третий вариант, потому что проще, не нужно менять процесс публикации и никто точно не ошибется таской и не сотрет исходный код.
Для этого нужно в (Preferenced -> Editor -> GUI Desinger) выбрать опцию, чтоб код сгененрированный попал в исходных кода класса (рис. 2), но Gradle не умеет менять исходники, поэтому в настройках (Preferenced -> Build -> Build Tools -> Gradle) нужно выбрать Intellij IDEA для сборки проекта(рис. 3). Навсегда оставлять такой выбор скорее не стоит, потому что IDEA может не сдружиться с некоторыми Gradle плагинам, но чтобы просто сгенерировать код подойдет.
Одним вечером допиливал корпоративный плагин, добавляя новые инструменты для работы с Compose. По ходу дела решил поднять версии kotlin, gradle и intellij plugin. Версии выбрал больше от балды, чтобы был доступен новый API. В итоге все сделал, проверил, пересобрал jar-ники, опубликовал и пошел спать.
Через пару дней прилетает жалоба, что плагин крашится при попытке запустить основную, но очень старую функциональность. Думал, что может я что-то сломал, случайно. Или старое апи выпили, которое мы использовали (как это обычно бывает), но я же собрал, протестировал все. Все было гуд, а теперь крашится даже у меня.
Ошибка такая:
Caused by: java.lang.NullPointerException: Cannot invoke "javax.swing.JList.setModel(javax.swing.ListModel)" because "$this$createPanel_u24lambda_u242_u24lambda_u241" is null
Первое, что пришло в голову, что возможно после поднятия версии kotlin начались какие-то приколы в старом коде, который взаимодействует с Java. Отчасти оказалось так. Такая ошибка из-за сильно вложенных вызовов apply, а один из них вызывался на null.Переписал код, чтобы избавиться от вложенный
apply и получил более очевидную ошибку:Caused by: java.lang.NullPointerException: Cannot invoke "javax.swing.JList.setCellRenderer(javax.swing.ListCellRenderer)" because "resources" is null
Тут нужно немного контекста. Инструмент это ToolWindow - это такая панель справа. Например, эмулятор, список Gradle Task так же являются ToolWindow. UI этого нашего ToolWindow написан с использованием GUI Designer, который выглядит как редактора xml в Android Studio, но результатом работы является java класс и описание ui в формате form, который под капотом на самом деле xml.В сгенерированном java коде описано содержимое формы, где название поля - это id ui компонента в форме. Выглядит это так:
public class GUIResListForm {
public JList<Resource> resources;
public JPanel panel;
}
И в этом же классе во время компиляции будет сгенерирован код, который будет конфигурировать ui, описанный в form.Возвращаясь к проблеме. Почему resources null, вроде все работало как часы, да и я в дебаге все тестил обычно. Запускаю снова в дебаг режиме плагин...и он работает, все инициализировано. Собираю jar-ник, устанавливаю и опять получаю ошибку.
Начинаю исследовать jar для дебага и публикации (отличаются таски для сборки: instrumentJar и просто jar) и эврика - просто нету кода, который собирает ui по форме) (рис. 1)
Тут можно было бы остановиться, запустить таску instrumentJar, собрать новые артефакты, опубликовать и пойти дальше, но хочется понять почему сломалось и возможно найти лучшее решение.
Так как поднималась версия intellij gradle плагина, который и отвечает за генерацию этого кода. Потыкал разные версии и обнаружил, что сломалась генерация с 1.12.0 по 1.13.0. Ну как сломалась? Просто вдруг в версии 1.13.0 начали выпиливать генерируемый код для форм в jar таске, а в instrumentJar оставили, поэтому у меня в дебаг версии все работало.
Выходит, что теперь никак не собрать jar таской плагин с генерируемым кодом. Мне стало интересно, как Jetbrains обошли это в своих семплах - там точно были примеры использования form. Но они там все уже выпили и вручную собирают ui.
Неприятно, но есть несколько вариантов решить проблему:
1. Сделать как в семплах, то есть отказаться от форм и все переписать.
2. Генерировать артефакты таской instrumentJar
3. Сгенерировать код для форм и добавить в исходный код формы.
Выбрал третий вариант, потому что проще, не нужно менять процесс публикации и никто точно не ошибется таской и не сотрет исходный код.
Для этого нужно в (Preferenced -> Editor -> GUI Desinger) выбрать опцию, чтоб код сгененрированный попал в исходных кода класса (рис. 2), но Gradle не умеет менять исходники, поэтому в настройках (Preferenced -> Build -> Build Tools -> Gradle) нужно выбрать Intellij IDEA для сборки проекта(рис. 3). Навсегда оставлять такой выбор скорее не стоит, потому что IDEA может не сдружиться с некоторыми Gradle плагинам, но чтобы просто сгенерировать код подойдет.
🗿3❤1
Пока в IDEA есть поддержка form это решение хорошее, но для нового UI стоит отдать предпочтение реализации вручную с помощью классов из Swing или дождаться Compose для IDEA.
А вот стоит ли использовать DLS 2 - непонятно. Поддержка пока не очень, а первая версия быстро устарела (тоже надо бы выпилить из проекта...).
А вот стоит ли использовать DLS 2 - непонятно. Поддержка пока не очень, а первая версия быстро устарела (тоже надо бы выпилить из проекта...).
❤2🗿1
Когда K2?
Наверное, все уже увидели недавние новости о компиляторе K2 в Kotlin. Google написали в блоге о своём опыте, Compose добавили поддержку, а JetBrains обещали супер скорость.
Супер-скорость возможно и будет, но позже. Хотя уже есть нетерпеливые, которые уже прогнали и измерили улучшения или их отсутствие.
Меня в новом плагине подкупало то, что он даёт супер крутой функционал по написанию расширений для языка, которые автоматически подхватывает idea. "Просто подключить jar'ник в компилятор Kotlin в IDEA и все заведётся" - думал я.
С этими размышлениями взялся переписывать свои backend плагины на frontend. Да, не стоило мне ожидать, что это будет просто, потому что fir и ir абсолютно два разных семантических дерева. Очень часто помогал класс Fir2IrConverter, но чаще всего приходилось самому додумывать.
Из интересного то, что нельзя получить доступ к fir, а нужно получать разные обертки над ним с помощью extension функции, которые иногда приватные, а иногда публичные - не понял закономерности некоторых случаем. Опять же нельзя к самим декларациям обращаться, нужно делать это extension функции и получать символы декларации, которые содержат информацию, которую можно вытащить в плагине.
Понятно почему такое ограничение возникло. Во frontend компиляторе несколько фаз, в которых можно делать по сути что-то одно: добавлять супертипы, проверять наследников, добавлять аннотации или производить проверки кода. И на какой то фазе могут быть не доступны декларации, которые не зарезолвлены в предыдущих фазах.
Я писал проверки кода, поэтому предполагал, что ничего к этому моменту незарезолвлено не будет.
В итоге, подрубаю свой плагин - ничего не происходит. IDEA ничего не подсвечивает. Полез в Slack за ответами (почему раньше не сделал, не знаю) и оказывается, что на данный момент поддержка fir в IDEA экспериментальная, а чтобы подрубить плагин нужно собрать IDEA из исходников и подложить свой плагин...
Но вообще можно подключить не в IDEA, а напрямую в проект через Gradle, как и backend плагин в принципе. И тогда ошибки и предупреждения будут подхватываться в панели сборки. Из преимуществ над backend плагином показываются ворнинги и работает навигация к проблемному участку кода.
Да, это не совсем то, чего я хотел, и чтобы не оставлять дело незавершенным и набив руку на поиск соответствии между двумя семантическими деревьями, переписал с Fir на традиционный PSI для IDEA плагина)
В итоге, мне fir менее зашёл чем ir плагин, но его потенциал скоро будет заметен как добавят поддержку в IDEA, а пока довольствуемся очень нестабилбным API.
К слову, уже на этом этапе у fir больше документации, чем у ir)
https://github.com/JetBrains/kotlin/blob/master/docs/fir/fir-basics.md
https://github.com/JetBrains/kotlin/blob/master/docs/fir/fir-plugins.md
Наверное, все уже увидели недавние новости о компиляторе K2 в Kotlin. Google написали в блоге о своём опыте, Compose добавили поддержку, а JetBrains обещали супер скорость.
Супер-скорость возможно и будет, но позже. Хотя уже есть нетерпеливые, которые уже прогнали и измерили улучшения или их отсутствие.
Меня в новом плагине подкупало то, что он даёт супер крутой функционал по написанию расширений для языка, которые автоматически подхватывает idea. "Просто подключить jar'ник в компилятор Kotlin в IDEA и все заведётся" - думал я.
С этими размышлениями взялся переписывать свои backend плагины на frontend. Да, не стоило мне ожидать, что это будет просто, потому что fir и ir абсолютно два разных семантических дерева. Очень часто помогал класс Fir2IrConverter, но чаще всего приходилось самому додумывать.
Из интересного то, что нельзя получить доступ к fir, а нужно получать разные обертки над ним с помощью extension функции, которые иногда приватные, а иногда публичные - не понял закономерности некоторых случаем. Опять же нельзя к самим декларациям обращаться, нужно делать это extension функции и получать символы декларации, которые содержат информацию, которую можно вытащить в плагине.
Понятно почему такое ограничение возникло. Во frontend компиляторе несколько фаз, в которых можно делать по сути что-то одно: добавлять супертипы, проверять наследников, добавлять аннотации или производить проверки кода. И на какой то фазе могут быть не доступны декларации, которые не зарезолвлены в предыдущих фазах.
Я писал проверки кода, поэтому предполагал, что ничего к этому моменту незарезолвлено не будет.
В итоге, подрубаю свой плагин - ничего не происходит. IDEA ничего не подсвечивает. Полез в Slack за ответами (почему раньше не сделал, не знаю) и оказывается, что на данный момент поддержка fir в IDEA экспериментальная, а чтобы подрубить плагин нужно собрать IDEA из исходников и подложить свой плагин...
Но вообще можно подключить не в IDEA, а напрямую в проект через Gradle, как и backend плагин в принципе. И тогда ошибки и предупреждения будут подхватываться в панели сборки. Из преимуществ над backend плагином показываются ворнинги и работает навигация к проблемному участку кода.
Да, это не совсем то, чего я хотел, и чтобы не оставлять дело незавершенным и набив руку на поиск соответствии между двумя семантическими деревьями, переписал с Fir на традиционный PSI для IDEA плагина)
В итоге, мне fir менее зашёл чем ir плагин, но его потенциал скоро будет заметен как добавят поддержку в IDEA, а пока довольствуемся очень нестабилбным API.
К слову, уже на этом этапе у fir больше документации, чем у ir)
https://github.com/JetBrains/kotlin/blob/master/docs/fir/fir-basics.md
https://github.com/JetBrains/kotlin/blob/master/docs/fir/fir-plugins.md
🔥1
Mobius
Информации, которую нарыл по компиляторным плагинам и, в частности, по frontend плагинам оказалось достаточно, чтобы доклад прошёл ревью.
Из интересного, что проекты, которые буду приводить в качестве примеров использования компиляторных плагинов, содержат ещё idea и gradle плагины. Не хватает flipper плагина для полного набора)
https://mobiusconf.com/talks/0beebbbd16bf4358ab2a1b60cabf57a1/
Информации, которую нарыл по компиляторным плагинам и, в частности, по frontend плагинам оказалось достаточно, чтобы доклад прошёл ревью.
Из интересного, что проекты, которые буду приводить в качестве примеров использования компиляторных плагинов, содержат ещё idea и gradle плагины. Не хватает flipper плагина для полного набора)
https://mobiusconf.com/talks/0beebbbd16bf4358ab2a1b60cabf57a1/
🔥2👍1
Инкрементальная компиляции в Compose Compiler. Что может пойти не так?
Погружаясь в исходники компилятора компоуза никогда не думал, что может возникнуть проблема с инкрементальной компиляцией. Но как-то изучая причины, по которым Compose Compiler определяет стабильность типов и то, как использует их в дальнейшей генерации - начали появляться сомнения, что все действительно гладко.
Начну с известного: Compose Compiler любезно определяет стабильность классов даже если не проставить на самом деле это одно и то же на данный момент , проставляя аннотацию
Но этим польза
Дело в том, что
Соответственно, нужно размечать классы в других модулях, чтобы повторно не пытаться определять стабильность класса с меньшим количеством информации, чем оно было доступно при полной компиляции.
Но тут есть две проблемы (или особенности):
1)(сюрприз) и на нестабильные классы. А информация о нестабильности или стабильности будет лежать в поле
Чтобы понять к чему могут привести эти особенности, нужно копнуть немного глубже в понятие стабильности в компиляторе.
В Compose Compiler не ограничивается опеределением того, что класс стабильный или нестабильный. Есть ещё несколько вариантов описания стабильности, но концептуально можно разделить на три:
1., а как это произойдёт совсем другая история .
1. Есть используются generic'и
2. Если он из другого модуля в виде stub и помечен аннотацией
Например. Для мемоизации лямбд важно, чтобы класс был точно стабильным, а
В итоге, суммируя вышесказанное, получаем первый кейс с багом для инкрементальной компиляции.
Первый кейс. Класс лежит в другом модуле и он стабилен, но публичный, а значит пометился аннотацией
Второй кейс скорее положительный.
Допустим класс из другого модуля нестабильный и используется аргументах
Ситуация становится ещё более неочевидной, если учесть, что инкрементальная компиляция работает не только на уровне
Ну и не забываем, что
Погружаясь в исходники компилятора компоуза никогда не думал, что может возникнуть проблема с инкрементальной компиляцией. Но как-то изучая причины, по которым Compose Compiler определяет стабильность типов и то, как использует их в дальнейшей генерации - начали появляться сомнения, что все действительно гладко.
Начну с известного: Compose Compiler любезно определяет стабильность классов даже если не проставить
@Stable и @Immutable@StabilityInferred.Но этим польза
@StabilityInferred не заканчивается. Дело в том, что
gradle модули компилируются, грубо говоря, изолировано друг от друга, поэтому компиляторный плагин при инкрементальной компиляции не может получить абсолютно всю информацию о содержимом классов/функции из другого модуля. Например, не получится вытащить body или переменные сгенерированные (я проверял). То есть доступно только stub отображение класса. Соответственно, нужно размечать классы в других модулях, чтобы повторно не пытаться определять стабильность класса с меньшим количеством информации, чем оно было доступно при полной компиляции.
Но тут есть две проблемы (или особенности):
1)
@StabilityInferred вешается val $stable: Int
2) Аннотация вешается только на публичные классыЧтобы понять к чему могут привести эти особенности, нужно копнуть немного глубже в понятие стабильности в компиляторе.
В Compose Compiler не ограничивается опеределением того, что класс стабильный или нестабильный. Есть ещё несколько вариантов описания стабильности, но концептуально можно разделить на три:
1.
Stable
2. Unstable
3. Runtime stable
Если с первыми двумя все понятно, то третий говорит о том, что стабильность класса не определена и это скорее произойдет в runtimeRuntime stable может быть класс:1. Есть используются generic'и
2. Если он из другого модуля в виде stub и помечен аннотацией
@StabilityInferred
Но в Compose Compiler используют runtime stable в разных случаях и по разному. В одних случаях он трактуется как стабильный, а в других как нестабильный. Например. Для мемоизации лямбд важно, чтобы класс был точно стабильным, а
runtime stable трактуется как unstable. А вот для генерации возможности пропуска функции во время рекомпозиции важно, важно, чтоб класс не был unstable, то есть runtime stable в этом случае приравнивается к stable.В итоге, суммируя вышесказанное, получаем первый кейс с багом для инкрементальной компиляции.
Первый кейс. Класс лежит в другом модуле и он стабилен, но публичный, а значит пометился аннотацией
@StabilityInferred и для текущего модуля является runtime stable. Текущий модуль компилируется и перед тем как обернуть лямбду в remember, смотрит: а не захватывается ли чего нестабильного? В данном случае runtime stabe == unstable, поэтому лямбда не будет сохраняться между рекомпозициями, хотя при чистой сборке сохранялась. Второй кейс скорее положительный.
Допустим класс из другого модуля нестабильный и используется аргументах
@Composable функции, а значит он будет расцениваться как stable и сгенерируется возможность пропуска рекомпозиции, хотя её не было при чистой сборке.Ситуация становится ещё более неочевидной, если учесть, что инкрементальная компиляция работает не только на уровне
gradle модулей, но и kotlin файлов и кейсы выше будут так же верны и для случаев, когда функция и класс находятся в разных файлах.Ну и не забываем, что
@StabilityInferred проставляется только для публичных классов, а в модуле могут быть ещё internal классы, которые будут stub'ами при инкрементальной компиляции. А Compose Compiler помечает stub'ы как unstable, если нету аннотации. В итоге получаем ещё новые баги.👍7🔥1
Третий кейс. Класс стабильный, но
Четвёртый кейс. Та же ситуация с классом, но в случае мемоизации лямбды. Класс был стабилен при чистой сборке и мемоизация работала, а теперь точно не работает, потому что
Собрав все свои наработки сделал PR в Compose Compiler, а потом перенёс его в android review, из-за отсутствия активности на GitHub.
Но тут большое спасибо Андрею Шикову, который проревьюил. Большинство моих фиксов ломали бинарную совместимость, поэтому фиксить будет команда компилятора сама.
Первый фикс:
Теперь аннотация
Появится возможность самому регулировать степень мемоизации лямбд и сохранять их, даже если они захватывают что-то нестабильное. Кроме того, можно будет зафорсить пропускаемость функции, даже если параметры функции нестабильные. А потом отдельной аннотацией
Мне понравились такие классные решения, поэтому свернул свои изменения. Но эти изменения ещё не влиты и возможно что-то поменяется.
Но некоторые фиксы, которые лежали на поверхности, не касались инкрементальной компиляции и ничего особо не ломали, все таки смог влить.
Проблема генерации $stable для combined stable классов. Да, все это время эта штука не работала из-за небольшого бага)
Мемоизация работала для function reference, у которых extension reciever нестабильный. Это не совсем правильно, а про reciever я писал тут.
internal и лежит в другом файле, но при инкрементальной компиляции становится нестабильным, а функция, которая имела возможность пропуска рекомпозиции, большее её не имеет.Четвёртый кейс. Та же ситуация с классом, но в случае мемоизации лямбды. Класс был стабилен при чистой сборке и мемоизация работала, а теперь точно не работает, потому что
internal класс стал unstable.Собрав все свои наработки сделал PR в Compose Compiler, а потом перенёс его в android review, из-за отсутствия активности на GitHub.
Но тут большое спасибо Андрею Шикову, который проревьюил. Большинство моих фиксов ломали бинарную совместимость, поэтому фиксить будет команда компилятора сама.
Первый фикс:
Теперь аннотация
@StabilityInferred будет добавляться и для internal классов, а в добавок, чтобы не даунгрейдить определение стабильности класса из stable до runtime stable, добавится аннотация @KnownStability
Второй фикс (скорее фича):Появится возможность самому регулировать степень мемоизации лямбд и сохранять их, даже если они захватывают что-то нестабильное. Кроме того, можно будет зафорсить пропускаемость функции, даже если параметры функции нестабильные. А потом отдельной аннотацией
@NonSkippableComposable выборочно отключать эту штуку для конкретных функций.Мне понравились такие классные решения, поэтому свернул свои изменения. Но эти изменения ещё не влиты и возможно что-то поменяется.
Но некоторые фиксы, которые лежали на поверхности, не касались инкрементальной компиляции и ничего особо не ломали, все таки смог влить.
Проблема генерации $stable для combined stable классов. Да, все это время эта штука не работала из-за небольшого бага)
Combined stable - это такой тип стабильности, когда она определяется через стабильность нескольких классов, которые используются в одном классе. В $stable в таком случае не может лежать какое то определённое значение, а должно было генерироваться логическое сложение $stable полей используемых классов. Мемоизация работала для function reference, у которых extension reciever нестабильный. Это не совсем правильно, а про reciever я писал тут.
GitHub
Fix lambda and function reference memorization and internal classes loosing stability on incremental compilation. Fix $stable field…
Proposed Changes
I have started to fix bug(https://issuetracker.google.com/issues/298965625) and have founded other incorrect cases
Lambda memoization for RuntimeStable types
Problem: On incrementa...
I have started to fix bug(https://issuetracker.google.com/issues/298965625) and have founded other incorrect cases
Lambda memoization for RuntimeStable types
Problem: On incrementa...
👍5
Убийца Compose?
Совсем недавно узнал, что Huawei делают аналог Compose Multiplatform в виде нового декларативного фреймворка arkUI.
Я хотел попробовать, но на сайте все ну прям очень пусто. Просто немного скриншотов не сильно хорошего качества.
Ещё я узнал от HR из Huawei, что на проект они захантили лидов Compose Multiplatform из JB. Но пока отнесся к этому со скепсисом, так как никаких официальных заявлений нету и у этих лидов в linkedin все ещё висит, что место работы JB.
Изначально предположил, что это будет форк Compose Multiplatform. Например, DevEco Studio, который является форком то ли IntelliJ Idea, то ли Android Studio. Но HR говорит, что проект прям с нуля, но будет продолжать идеи Compose Multiplatform, хоть основным языком будет Js/Ts.
В общем жду. Будет интересно глянуть)
Совсем недавно узнал, что Huawei делают аналог Compose Multiplatform в виде нового декларативного фреймворка arkUI.
Я хотел попробовать, но на сайте все ну прям очень пусто. Просто немного скриншотов не сильно хорошего качества.
Ещё я узнал от HR из Huawei, что на проект они захантили лидов Compose Multiplatform из JB. Но пока отнесся к этому со скепсисом, так как никаких официальных заявлений нету и у этих лидов в linkedin все ещё висит, что место работы JB.
Изначально предположил, что это будет форк Compose Multiplatform. Например, DevEco Studio, который является форком то ли IntelliJ Idea, то ли Android Studio. Но HR говорит, что проект прям с нуля, но будет продолжать идеи Compose Multiplatform, хоть основным языком будет Js/Ts.
В общем жду. Будет интересно глянуть)
Harmonyos
ArkUI
ArkUI is a declarative UI development framework for building HarmonyOS application UIs. It offers simple UI information syntax, a wide range of UI components, and responsive live previewer, to boost your HarmonyOS app UI development efficiency by 30%. With…
🤔3
Небольшой квиз :) Как думаете, есть ли разница между лямбдой и ссылкой на функцию в качестве аргумента Composable функции?
Anonymous Quiz
25%
Нет
75%
Есть