<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Hello World</title><generator>teletype.in</generator><description><![CDATA[Размышления на тему науки, программирования и высоких технологий.]]></description><image><url>https://teletype.in/files/5a/9a/5a9abc7b-226b-4cd0-b291-46194a43a0f7.png</url><title>Hello World</title><link>https://teletype.in/@hw_code</link></image><link>https://teletype.in/@hw_code?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/hw_code?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/hw_code?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Thu, 23 Apr 2026 14:54:33 GMT</pubDate><lastBuildDate>Thu, 23 Apr 2026 14:54:33 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@hw_code/OO9ReLSr6</guid><link>https://teletype.in/@hw_code/OO9ReLSr6?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code</link><comments>https://teletype.in/@hw_code/OO9ReLSr6?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code#comments</comments><dc:creator>hw_code</dc:creator><title>Flat map в Python 🐍</title><pubDate>Fri, 19 Mar 2021 13:07:20 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/38/83/388336cd-f68a-4756-891a-9f616ab2033e.png"></media:content><category>python</category><description><![CDATA[<img src="https://teletype.in/files/0c/7a/0c7ad400-3ab2-431c-9299-7953a9bb1bcd.png"></img>Некоторое время назад мы касались вопросов функционального программирования на Swift в контексте монад и функторов (flatMap там тоже был). Теперь давайте рассмотрим концепцию flat_map в языке Python.]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/0c/7a/0c7ad400-3ab2-431c-9299-7953a9bb1bcd.png" width="1196" />
  </figure>
  <p>Некоторое время назад мы касались вопросов функционального программирования на Swift в контексте монад и функторов (<code>flatMap</code> там тоже был). Теперь давайте рассмотрим концепцию <code>flat_map</code> в языке Python.</p>
  <p>Ну, здесь нет built-in функции <code>flat_map</code>. Но её можно имплементировать самостоятельно, причём несколькими разными способами. Поэтому, вместо того чтобы дискутировать на тему на создания «pure pythonic» функции <code>flat_map</code>, давайте лучше рассмотрим более интересный вопрос:</p>
  <blockquote>Как создать функцию <code>flat_map</code> в python наиболее эффективным способом?</blockquote>
  <h2>Определение</h2>
  <p>Начнём с определения. <code>flat_map</code> это операция, которая принимает список элементов типа <code>A</code> и функцию <code>f</code> типа <code>A -&gt; [B]</code>. Функция <code>f</code> таким образом применяется ко всем элементам первоначального списка, и затем все результаты конкатенируются. Простой пример:</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/c1/59/c1594a09-8023-4f9f-a078-cf3a6133fdf7.png" width="598" />
  </figure>
  <p>Хотя данная операция довольно простая, идея, лежащая в её основе называется «<a href="https://wiki.haskell.org/Monad" target="_blank">монада</a>». Насколько я знаю, есть 4 простых способа создать <code>flat_map</code> в Python:</p>
  <ul>
    <li>цикл for и extend</li>
    <li>двойной list comprehension</li>
    <li>map и reduce</li>
    <li>map и sum</li>
  </ul>
  <h2>Измерение</h2>
  <p>Какой подход будет наиболее эффективным? Чтобы ответить на этот вопрос, я собираюсь проверить время исполнения программы в 3 различных случаях:</p>
  <p>a) 100 списков, каждый содержит 10 чисел типа int<br />b) 10 000 списков, каждый содержит 10 чисел типа int<br />c) 10 000 списков, каждый содержит 10 объектов (инстансов класса)</p>
  <p>Чтобы сделать это, я буду использовать функцию <code>check_time</code>  и дополнительную простую функцию <code>id</code> для её передачи в <code>flat_map</code>:</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/cc/e4/cce4a442-2a69-4bad-a2f8-cf05123aa3c8.png" width="646" />
  </figure>
  <p>Весь используемый код можно найти по <a href="https://pastebin.com/fn5Emg2R" target="_blank">ссылке</a>.</p>
  <h2>Map reduce</h2>
  <p>Наиболее «функциональный» ответ на поставленную задачу – это комбинация <code>map</code> и <code>reduce</code>. Потребуется импорт библиотеки <code>functools</code> (<a href="https://docs.python.org/3.8/library/functools.html" target="_blank">документация</a>), так что даже при всей его крутизне, это всё таки не решение в одну строчку.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/0f/3d/0f3d8ec8-aef1-458a-a884-49db7efd2601.png" width="662" />
  </figure>
  <p><strong>Результат</strong>:<br />a) 0.1230 мс<br />b) 1202.4405 мс<br />c) 3249.0119 мс</p>
  <p>Тут <code>map</code> можно легко заменить на list comprehension, но особой разницы заметно не будет.</p>
  <h2>Map sum</h2>
  <p>Другое «функциональное» решение – это использовать <code>sum</code>. Мне нравится этот подход, но его производительность также оставляет желать лучшего. Результаты в принципе не лучше, чем в предыдущем случае.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/49/9a/499af3fc-0995-49cd-b9a8-bb754b7d6c2f.png" width="502" />
  </figure>
  <p><strong>Результат</strong>:<br />a) 0.1080 мс<br />b) 1150.4400 мс<br />c) 3296.3167 мс</p>
  <h2>For extend</h2>
  <p>До сих пор наши имплементации функции <code>flat_map</code> работали относительно быстро для малого размера списка, и были медленными для большого. Однако данная «классическая» имплементация в 1000 раз быстрее! Жаль конечно что это не решение в одну строчку. Но, наверное, это относительно небольшая цена за такую производительность.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/86/7c/867cc79f-416e-448e-af61-25eea01d4bd0.png" width="502" />
  </figure>
  <p><strong>Результат</strong>:<br />a) 0.0211 мс<br />b) 2.4535 мс<br />c) 2.7347 мс</p>
  <p>Интересно то, что разница между скоростью работы <code>flat_map</code> в случае со списком интов и списком классов теперь не такая уж и большая.</p>
  <h2>List comprehension</h2>
  <p>Настоящий «pythonic» подход (и в принципе тоже функциональный). Единственный минус данной реализации, это слегка запутанный двойной <code>for</code> цикл.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/0c/7a/0c7ad400-3ab2-431c-9299-7953a9bb1bcd.png" width="598" />
  </figure>
  <p><strong>Результат</strong>:<br />a) 0.0372 мс<br />b) 4.1477 мс<br />c) 4.5945 мс</p>
  <p>Как мы видим, производительность действительно потрясающая. Немного медленнее, чем в предыдущем варианте, но теперь то это настоящее решение в одну строчку.</p>
  <p>Давайте посмотрим что будет если мы заменим list comprehension на generator expression в последнем примере:</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/73/20/73206305-0ff8-443e-9ae3-da1b6e2ca863.png" width="598" />
  </figure>
  <p>Результат:<br />a) 0.0008 мс<br />b) 0.0008 мс<br />c) 0.0007 мс</p>
  <p>Так вот он наш победитель? А стоп, мы не развернули само выражение. Давайте развернем, чтобы проверить его реальную производительность. Кроме того, развернем и list comprehension, для того, чтобы посмотреть как ведёт себя код в идентичных условиях:</p>
  <p><strong>list comprehension</strong>:<br />a) 0.0537 мс<br />b) 5.3700 мс<br />c) 5.7781 мс</p>
  <p><strong>generator expression</strong>:<br />a) 0.1512 мс<br />b) 13.0335 мс<br />c) 13.3203 мс</p>
  <p>Таким образом, мы видим, что первоначальное ускорение было ошибкой: код не был выполнен, поэтому замер времени был некорректен. Всегда помните о том, что реальная производительность (в контексте какой-либо конкретной задачи) может быть гораздо ниже.</p>
  <h2>Заключение</h2>
  <figure class="m_column">
    <img src="https://teletype.in/files/6b/29/6b296fd2-22dc-45d5-a481-15c7c0e0f51c.png" width="798" />
  </figure>
  <p>Простая проблема, много решений, но только один победитель... ну или два. Я считаю разумным отдать первое место как for+extend, так и list comprehension. Первый вариант хорош тогда, когда ваша главная забота – производительность. Второй вариант – это действительно хороший выбор, когда вам нужно удобное решение в одну строчку.</p>
  <p>Поэтому в следующий раз, когда вам нужно будет применить <code>flat_map</code> в Python, просто используйте list comprehension. И самое главное, не поддавайтесь соблазну использовать <code>reduce</code> или <code>sum</code> для длинных списков!</p>
  <p><a href="https://dev.to/turbaszek/flat-map-in-python-3g98" target="_blank">Оригинальный текст</a></p>
  <hr />
  <p>Статья подготовлена для канала <a href="http://t.me/hw_code" target="_blank">Hello World</a>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@hw_code/6CNR4KoAY</guid><link>https://teletype.in/@hw_code/6CNR4KoAY?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code</link><comments>https://teletype.in/@hw_code/6CNR4KoAY?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code#comments</comments><dc:creator>hw_code</dc:creator><title>Агрегация vs Композиция</title><pubDate>Thu, 18 Mar 2021 20:56:40 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/e7/64/e7647615-de78-4d41-9dff-904c3914cef7.jpeg"></media:content><category>swift</category><description><![CDATA[<img src="https://teletype.in/files/04/b6/04b64f16-4e84-44bb-adaf-543e5797a292.jpeg"></img>Решил набросать небольшой пост по ООП... Вот пример композиции и агрегирования с точки зрения кода, написанного на Swift (на Python можно сделать по аналогии). Посмотрев на этот код, вы сможете оценить разницу между ними.]]></description><content:encoded><![CDATA[
  <figure class="m_original">
    <img src="https://teletype.in/files/04/b6/04b64f16-4e84-44bb-adaf-543e5797a292.jpeg" width="366" />
  </figure>
  <h2>Вместо введения</h2>
  <p>Решил набросать небольшой пост по ООП... Вот пример композиции и агрегирования с точки зрения кода, написанного на Swift (на Python можно сделать по аналогии). Посмотрев на этот код, вы сможете оценить разницу между ними.</p>
  <p>Кстати, в объектно-ориентированном дизайне композиция предпочтительнее наследования, о чем я говорил и в <a href="https://teletype.in/@hw_code/g6d2hr2pv" target="_blank">прошлой статье</a>, и даже джавист Джошуа Блох заявлял о её важности в классической книге по Java (Effective Java).</p>
  <p>Начать стоит вообще с того, что и агрегация и композиция – это подклассы ассоциации, ну или если можно так выразиться «частные случаи» её проявления. Ассоциация по сути говорит о взаимоотношении с объектом.</p>
  <p>Когда мы говорим об ООП, мы всегда думаем об объектах (экземплярах класса), классе (своеобразном «blueprint&#x27;е» объектов) и отношениях между ними. Объекты связаны и взаимодействуют друг с другом с помощью методов. Если объект одного класса может использовать методы, предоставляемые объектом другого класса, то такие отношения называются ассоциациями.</p>
  <p>Ну ладно, довольно болтологии. Как говорил Линус Торвальдс,</p>
  <blockquote>Talk is cheap. Show me the code.</blockquote>
  <h2>Агрегация</h2>
  <p>Агрегация, или её ещё называют «слабая ассоциация» – это такой тип отношений, когда у класса есть объект, который он позаимствовал где-то еще (например у другого класса).</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/43/c2/43c25da1-71e5-40ee-9145-5a5a389a34c4.png" width="550" />
  </figure>
  <p>Вот например как на картинке выше. Если инстанс (объект) класса <code>Library</code> будет уничтожен, <code>books</code> будет продолжать своё существование, и наоборот.</p>
  <p>Если не нравится пример с книгами, вот тут мой любимый пример с машинками и двигателями. Здесь важно понимать главное, «слабая ассоциация» подразумевает, что <code>Car</code> не владеет <code>Engine</code>. <code>Engine</code> создается где-то вовне, и передается внутрь <code>Car</code>. Это и есть агрегация.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/22/d8/22d8217f-51a0-4ccd-a13e-0d045bf98630.png" width="550" />
    <figcaption>машинка делает вжжжж</figcaption>
  </figure>
  <h2>Композиция</h2>
  <p>Композиция, или «сильная ассоциация» – это такой тип отношений, когда класс именно «владеет» объектом и несёт ответственность за его время жизни.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/2e/75/2e755f6d-e706-4916-82af-1d2d37e6a56a.png" width="550" />
  </figure>
  <p>Теперь же если инстанс (объект) класса <code>Reader</code> будет уничтожен (деинициализирован), вместе с ним умрёт и <code>books</code>. Как вы уже могли заметить, в этом примере ничего не передается внутрь <code>Reader</code>. Все объекты класса <code>Book</code> создаются внутри самого <code>Reader</code>.</p>
  <p>А вот и пример с машинками. Поэтому композиция еще называется «сильной» ассоциацией, в этом случае <code>Car</code> именно «владеет» <code>Engine</code> и ответственен за время жизни его инстанса, если уничтожить инстанс класса <code>Car</code>, пропадет и созданный внутри него инстанс <code>Engine</code>.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/61/d8/61d863b4-1acc-4072-a67c-417b3bc5c814.png" width="550" />
  </figure>
  <h2>Заключение</h2>
  <p>Грамотный выбор типа отношений между классами и объектами – залог успеха любого ООПшника! Давайте еще раз кратко подытожим все вышесказанное.</p>
  <ul>
    <li><strong>Агрегация</strong> подразумевает такой тип отношений, в которых дочерняя структура может существовать независимо от родительской. Пример: <code>Person</code> (родительский класс) и <code>Student</code> (дочерний класс). Если <code>Person</code> будет удалён, <code>Student</code> всё равно продолжит существовать.</li>
    <li><strong>Композиция</strong> подразумевает такой тип отношений, в которых дочерняя структура не может существовать отдельно от родительской. Пример: <code>House</code> (родительский класс) и <code>Room</code> (дочерний класс). <code>Room</code> не существует отдельно от <code>House</code>.</li>
  </ul>
  <p>Если логика задана так, что дочерней структуре необходимо существовать вне зависимости от родительской, значит нужно использовать агрегацию. Если нет – композицию.</p>
  <hr />
  <p>Статья подготовлена для канала <a href="http://t.me/hw_code" target="_blank">Hello World</a>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@hw_code/G3fJJhb8O</guid><link>https://teletype.in/@hw_code/G3fJJhb8O?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code</link><comments>https://teletype.in/@hw_code/G3fJJhb8O?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code#comments</comments><dc:creator>hw_code</dc:creator><title>dev talks #2</title><pubDate>Sun, 07 Feb 2021 18:57:35 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/9f/c8/9fc88f1a-4ba2-4a6f-8b8e-4d3420924d13.png"></media:content><category>dev talks</category><description><![CDATA[<img src="https://teletype.in/files/6a/2c/6a2cbcdd-4ff7-412e-adf4-2a57254fbd73.png"></img>Сегодня мы продолжим говорить о том, как устроена мобильная разработка крупных российских продуктов. В гостях у нас, как и в тот раз, тимлид iOS команды разработки Cardsmobile – Богдан Маншилин. В предыдущей статье мы разбирали несколько крупных российских компаний: Яндекс.Еду, Ламоду и Ultimate Guitar. Теперь мы поговорим о том, как попасть в такую компанию, а также о проблемах этих компаний.]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/6a/2c/6a2cbcdd-4ff7-412e-adf4-2a57254fbd73.png" width="1200" />
  </figure>
  <p>Сегодня мы продолжим говорить о том, как устроена мобильная разработка крупных российских продуктов. В гостях у нас, как и в тот раз, тимлид iOS команды разработки Cardsmobile – Богдан Маншилин. В <a href="https://teletype.in/@hw_code/SL3hjRvZW" target="_blank">предыдущей</a> статье мы разбирали несколько крупных российских компаний: Яндекс.Еду, Ламоду и Ultimate Guitar. Теперь мы поговорим о том, как попасть в такую компанию, а также о проблемах этих компаний.</p>
  <hr />
  <p><em>Вот допустим, я iOS-разработчик. Как мне попасть в крупную российскую компанию?</em></p>
  <p>Ну, в первую очередь нужно подать туда резюме, чтобы тебя вообще в эту компанию рассмотрели (смеётся). Хотя, конечно, вопрос очень интересный, поскольку сразу возникает ещё куча наводящих вопросов. Какой у тебя сейчас уровень? Готовы ли тебя туда рассматривать или нет? Вот, например, когда я пытался пробиться в Ultimate Guitar, мой уровень был совсем джуновским. В этой истории интересно, что мне <em>на самом деле</em> хотелось туда попасть. Я листал вакансии и видел, что есть компании, которые занимаются какой-то разработкой, чем-то непонятным. У них есть какие-то приложения, которыми никто не пользуется.</p>
  <p>И вот смотрю – Ultimate Guitar. Меня тогда заинтересовало то, что я сам в то время учился играть на гитаре, и, в общем-то, продукт мне очень понравился. И я тогда подумал: окей, но как же мне, junior-разработчику, попасть в такую крутую компанию? Учитывая то, что у них было написано, что они рассматривают кандидатов от уровня middle и выше (от 3 лет опыта и выше). У меня на тот момент опыта работы был неполный год. Я подошел к этому всему с творческой точки зрения. Я решил, что если я просто скину свое резюме, где написано про неполный год опыта, где я что-то пытался делать с переменным успехом, то, скорее всего, ничего не получится. Они получают сотни таких резюме в день, и вряд ли вообще обратят на меня внимание.</p>
  <p>Возникает вопрос: как же сделать так, чтобы на меня обратили внимание? Единственным решением, которое на тот момент я придумал, было создать, условно говоря, сайт о себе. Я знал: есть ребята, которые делают не просто резюме, а создают крутые сайты, где есть интерактив, и там можно узнать о человеке, посмотреть его портфолио. Очень часто такие сайты делали дизайнеры. Я видел как javascript-разработчик создал небольшую игру в браузере: там на машинке подъезжаешь к разным точкам и смотришь разные факты о нем. Меня это заинтересовало, и я подумал, что было бы неплохо сделать нечто подобное. Возникла идея, которую я реализовал где-то за 2 дня. Вышло, правда, немного топорно, но уже с приемлемым качеством.</p>
  <h2>Примешь синюю таблетку – и сказке конец</h2>
  <p>Это был <a href="https://github.com/AV8R-/oldCV" target="_blank">небольшой сайт</a>, развернутый на github pages. Написал я его следующим образом: просто взял уже готовый фреймворк, который отображал сайт с каруселью картинок, на которых я описал 5 своих soft скиллов.</p>
  <blockquote>Конечно это сейчас я бы их назвал soft скиллами, а тогда я описал 5 отличительных черт, характеристик самого себя.</blockquote>
  <figure class="m_column">
    <img src="https://teletype.in/files/61/fa/61fa144c-3bce-4f82-ae6c-ebce42775c94.png" width="1675" />
  </figure>
  <p>Причём описывал я их таким образом, как будто меня описывает со стороны какой-то другой человек, например, мама, бабушка, неважно. Это выглядело максимально нагло, будто бы я такой молодец и красавчик. Кроме того, я записал аудио, где я что-то проговариваю по каждому слайду, что имею в виду под той или иной характеристикой, привожу примеры. В конце, конечно, музыка, надпись «спасибо за просмотр» и мои контакты для связи. А чтобы у рекрутеров было и нормальное резюме, я сделал главную страницу в виде Морфеуса из «Матрицы», протягивающего две таблетки – красную и синюю. На красной – презентация. А синяя была подписана как «скучное резюме». По этой ссылке и скачивалось то резюме, в котором ничего не было, год работы с такими-то фреймворками и всё.</p>
  <p>На удивление, это сработало. История закончилась тем, что я стал единственным junior-разработчиком, которого Ultimate Guitar релоцировал из другого региона России.</p>
  <p><em>Сказывалось ли на твоей дальнейшей работе отсутствие опыта? Ты же говоришь, что им требовался мидл с 3 годами опыта, а ты на тот момент был джуном с неполным годом.</em></p>
  <p>Мешало, но не сильно. Секрет прост: работаешь с 9 утра до 9 вечера, 6 дней в неделю, иногда остаешься после работы. Вот тогда всё нормально. Тогда можешь соответствовать уровню мидла, и выполнять тот же уровень работы за день.</p>
  <p><em>То есть, ты постарался и нагнал?</em></p>
  <p>Работая в таком темпе, уже через год-полтора я чувствовал, что вполне соответствую позиции middle-разработчика, а потом ушёл в другую компанию.</p>
  <p><em>А как у вас обстоят дела в Cardsmobile? Как попасть сейчас в вашу компанию?</em></p>
  <p>В Cardsmobile, наверное, всё-таки чуть попроще (смеется). Не нужно кидать такое резюме. Хотя на данный момент, даже если junior-разработчик нам сильно понравится, мы просто физически не сможем его взять. Просто у нас немного разные подходы. Если Ultimate Guitar, в принципе, были готовы брать джунов, даже когда у них не было ресурсов на их обучение, то мы пока относимся к этому с бо́льшей ответственностью. Если мы берем джуна, значит мы будем его развивать. То есть, развиваться – это не задача самого джуна, это наша задача – передать ему знания. Джунов мы временно не рассматриваем, но на позиции middle-разработчик или senior-разработчик достаточно просто скинуть мне резюме. У нас много открытых вакансий. В связи с выходом на международные рынки (в Европу) мы расширяем штат и нанимаем много разработчиков с уровнем от мидла. То есть хотя бы от двух лет опыта работы. Вакансии можно найти на hh или спросить у меня в ЛС в Telegram.</p>
  <h2>Проблемы российских компаний</h2>
  <p><em>Вот такой, немного спорный вопрос. На твой взгляд, есть ли у российских компаний какие-либо проблемы? И если есть, то какие? Речь идет, в первую очередь, о проблемах, связанных с технической частью.</em></p>
  <p>Для начала, давай проясним, что ты имеешь в виду под «техническими проблемами»?</p>
  <p><em>Ну вот допустим, на твой взгляд, можно было бы какую-то проблему решить более лучшим способом, чем она решается сейчас?</em></p>
  <p>Ну, смотри. Я всё равно разделяю такие проблемы на 2 типа. Первый тип – это скорее какие-то проблемы-задачи. Вот у нас там куча проблем. Нам нужно построить нормальные процессы в командах, нам нужно вырасти по количеству разработчиков в два раза, нам нужно устаканить архитектурные вопросы в команде. Вот это всё скорее не проблемы, а задачи. Мы примерно знаем, как их решать, или по крайней мере осознаём, что такие проблемы есть. Мы смотрим на варианты их решения и в меру наших ресурсов решаем.</p>
  <p>А есть проблемы, условно говоря – дисфункции. Что-то системно мешает нам добиваться лучшего результата. Несколько таких примеров я могу привести из Ламоды и Ultimate Guitar того образца. Что интересно, такие дисфункции зависят от того, с какой точки на них смотреть. С одной стороны это – проблема, а с другой – преимущество. Вот например, Ламода – это компания с товарищеским стилем управления. То есть, там построен такой safe-space, разработчиков там не наказывают и относятся очень лояльно. Всех очень любят и не тревожат, и ты можешь работать в очень комфортных для себя условиях. Это плюс для Ламоды, по сути это даже является некой частью их культурного кода. Если подумать о том, что такое Ламода, то в первую очередь приходит в голову мысль, что здесь комфортно и хорошо. Есть safe-space, все очень дружелюбные и работать приятно.</p>
  <p>Однако, с моей точки зрения подобный подход немного расхолаживает, расслабляет. Если говорить о том количестве достижений, которое у меня было в том же Ultimate Guitar, когда я был джуном, то оно гораздо больше, чем количество достижений в Ламоде. Я мог позволить себе расслабиться и спокойно работать в своём темпе, и постепенно это расхолаживало. С каждым разом я всё медленнее и медленнее работал. А вот в Ultimate Guitar того периода всё было по-другому. Интересно, что Ultimate Guitar очень активно нанимал джунов, несмотря на то, что их развитием никто особо не занимался.</p>
  <h2>Удалил ветку develop</h2>
  <p>Я вспомнил очень забавный случай. Когда я только пришел в Ultimate Guitar, в какой-то момент я не очень хорошо умел обращаться с гитом (git, система контроля версий). За весь мой год опыта работы (до Ultimate Guitar), я практически всё время работал сам, разве что иногда в паре. Мы использовали svn, и конфликтов там особо не было. Я сам коммитил и сам с собой работал в репозитории. А тут я пришёл в большую команду, и с какими-то коммитами законфликтовал. Не помню, в чём была история, но однажды, решая проблему с репозиторием, я пришел к тому, что удалил у себя ветку develop, правда, локально.</p>
  <p><em>Локально?</em></p>
  <p>Да-да (смеётся). Очень повезло, что я сделал это локально. Я тогда позвал старших ребят, и все посмеялись. А я сам чуть не побелел тогда. Основная ветка была удалена.</p>
  <p>Наверное, это такой риск для компании, когда мы берем джунов, и прям бросаем их в воду. Сами разбирайтесь во всем этом, сами плавайте. Он либо выплывет, либо потонет. Мало того, что он может выплыть или потонуть, так может ещё и нанести какой-то урон компании. Мне вот повезло, что я его не нанёс и меня не уволили. Всё закончилось хорошо.</p>
  <p>Однако в тот момент проблема заключалась в том, что в компании была построена культура «развивайся или умри». Всё было заточено на рост, все должны были развиваться. Это было требование к каждому из сотрудников: через полгода-год ты должен стать лучше. Однако никакого прозрачного процесса, никакой помощи в развитии на тот момент не было. Были задачи. Вся помощь заключалась в том, что тебе ставили сверхамбициозные цели. То есть ставили цели таким образом, чтобы ты не мог их выполнить. А дальше сиди и разбирайся, выкручивайся сам, чтобы их выполнить. И этот подход, несмотря на то, что он должен был стимулировать людей находить новые решения, развиваться, по факту, стимулировал (лично меня) работать больше. Больше и больше. Сначала я работал с 9 утра до 9 вечера, потом в какой-то момент, когда мы пилили приложение на React-Native, я работал с 9 утра до 4 утра, и всё равно не успевал ничего сделать. Еле-еле мы успели к моменту демо этого прототипа для менеджеров Apple, и потом я, фух, выдохнул. Однако для меня цель была на тот момент неподъёмной. Даже не знаю, каким образом я её достиг. Наверное, с каким-то количеством вреда для своего здоровья...</p>
  <p><em>А это вообще нормальная практика в компаниях?</em></p>
  <p>Какая именно?</p>
  <p><em>Ну вот, сидеть с 9 и до 9.</em></p>
  <p>Наверное, не очень. Вот Ultimate Guitar сразу в этом признаётся, когда ты к ним приходишь. Они говорят, что все ребята горят проектами и работают до тех пор, пока задача не закончена. Если ты закончишь свои задачи раньше, пожалуйста, уходи раньше. Не закончишь, ну... надо закончить. Я больше такого подхода, наверное, не встречал. Они сами говорят: «мы не для всех». Для тех, кто хочет работать в таком темпе, пожалуйста: мы предоставляем амбициозные цели, а вы сами их решаете и развиваетесь. В Ламоде или Яндекс.Еде такого не встречалось. Хотя, в Яндекс.Еде, возможно, бывали моменты, когда есть какая-то срочная задача, нужно прям вот-вот её решить, к тебе приходят и говорят, вот надо. И ты сидишь и решаешь, а потом менеджер тебе пиццу заказывает.</p>
  <p>Сейчас я, например, частенько перерабатываю. Но я бы хотел, чтобы эти переработки исходили от меня самого. То есть я управляю своим личным временем, и часть этого времени я мог бы инвестировать в своё личное развитие. Для достижения целей, или, может, мне нравится какая-либо задача и я хочу её закончить. Почему бы и нет. И при этом я могу сам управлять этим временем. Вот сейчас я перерабатываю, а завтра я точно по графику закончу и буду заниматься своими делами или отдыхать, чтобы не выгореть. Иначе, если постоянно так работать, это неизбежно приведёт к выгоранию.</p>
  <p><em>А у тебя, как у тимлида, вообще какие обязанности?</em></p>
  <p>Мои обязанности находятся на уровне проблем. Мне говорят: «есть такая проблема». Есть задача и она должна быть решена. Условно, ко мне приходят и говорят: «нужно, чтобы приложение было готово к выходу на европейский рынок». А дальше сиди и думай, собственно, что же означает «выход на Европу», с кем же нужно поговорить, от кого добиться требований, как это должно выглядеть, и кому эти задачи потом раздать.</p>
  <p>Есть задача «найми 7 разработчиков». Идешь и думаешь, как же мне нанять 7 разработчиков? Может быть, тех бренд построить, может, пошерстить по контактам, может кого-то из предыдущих коллег приманить, и т.д. Таких задач довольно много, я могу их делать сам или кому-то делегировать. Вот как-то так.</p>
  <h2>Хакнул Swift</h2>
  <p><em>А что тебе запомнилось в каждой из компаний? Какие были интересные моменты?</em></p>
  <p>В Ultimate Guitar мне очень ярко запомнился один момент. Благодаря тому, что там очень драйвовая культура, ставили амбициозные цели, и я «хакнул» Swift. В общем, задача заключалась в том, что у нас были две кастомные клавиатуры и одна нативная. Они между собой переключались. То есть, вот есть редактор аккордов, я пишу текст, слова песни. Потом я должен переключиться на клавиатуру с аккордами, или с табами, ещё с чем-то. То есть, всего 3 типа клавиатур. Эти клавиатуры должны были свайпаться слева направо. А нативная клавиатура поднималась снизу вверх. Выглядело это так: две боковые клавиатуры свайпаются слева направо, а посерединке есть пустое место, в которое приезжает снизу вверх нативная клавиатура. Выглядело максимально не очень, и мы думали, что с этим можно сделать. В iOS есть уведомление о движении клавиатуры, в которой зашита информация о том, какая будет анимация у данного действия. Перед началом анимации мы можем получить начальный и конечный фреймы клавиатуры. Однако в Swift это let константы, и никак изменить мы их не можем. Я подумал, что, если это let константы, значит, они где-то находятся в памяти, то есть хранятся в какой-то ячейке. А что, если я узнаю эту ячейку памяти и банально перетру эти значения? Просто пойду в эту ячейку памяти и запишу туда другие. В Swift есть биндинги в сишные функции, например, мне тогда понадобился memcpy. И, знаешь, это сработало. Просто банальным образом, перед началом анимации я заменил в ячейках памяти фреймы на нужные, и анимация стала работать не снизу вверх, а слева направо или справа налево, как и было нужно. В тот момент я поблагодарил себя самого за то, что я сам развиваюсь и смотрю на разные инженерные области, кроме, непосредственно, iOS.</p>
  <p><em>То есть, подобный хак дальше пошел в продакшен?</em></p>
  <p>Да-да. Я об этом никому не рассказывал, потому что боялся, что Apple увидит и отклонит приложение. Какое-то время это работало в продакшене, но, к сожалению, этого приложения уже нет, это было приложение авторов табов, его уже удалили из стора, потому что им мало кто пользовался.</p>
  <p><em>Я кстати слышал, что некоторые разработчики Cardsmobile читают мой канал в Telegram.</em></p>
  <p>Кто эти разработчики, покажите мне их пальцем (смеется). Ну, конечно кто-то наверняка читает. С недавних пор этот канал читаю и я.</p>
  <p><em>Хорошо, тогда на этой позитивной ноте мы и закончим наш разговор. Я думаю получилось очень здорово, и спасибо за интересный рассказ, Богдан!</em></p>
  <p>Спасибо за приглашение! Мне тоже было приятно.</p>
  <hr />
  <p><a href="http://t.me/JamSao" target="_blank">Контакт</a> Богдана в Telegram, если кого-то вдруг заинтересует предложение поработать в Cardsmobile.</p>
  <hr />
  <p>Статья подготовлена для канала <a href="https://t.me/hw_code" target="_blank">Hello World</a>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@hw_code/g6d2hr2pv</guid><link>https://teletype.in/@hw_code/g6d2hr2pv?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code</link><comments>https://teletype.in/@hw_code/g6d2hr2pv?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code#comments</comments><dc:creator>hw_code</dc:creator><title>Композиция &gt; наследование</title><pubDate>Wed, 03 Feb 2021 15:40:04 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/14/af/14af64fa-cf11-49bf-a9f3-31170a5870f2.png"></media:content><category>swift</category><description><![CDATA[<img src="https://teletype.in/files/dc/35/dc3593f7-af64-44be-8cdb-1724baa55131.png"></img>У меня давно назревала подобная статья, еще примерно с момента осознания того, почему синглтон это плохо (может когда-нибудь будет статья и на эту тему).]]></description><content:encoded><![CDATA[
  <figure class="m_retina">
    <img src="https://teletype.in/files/dc/35/dc3593f7-af64-44be-8cdb-1724baa55131.png" width="444" />
    <figcaption>Диаграмма наглядно показывает, как можно удобно задизайнить гибкое поведение объекта animal с поведением fly и sound с помощью композиции</figcaption>
  </figure>
  <p>У меня давно назревала подобная статья, еще примерно с момента осознания того, почему синглтон это плохо (может когда-нибудь будет статья и на эту тему).</p>
  <blockquote>Для умников в комментах: глобальный мутабельный стейт – это плохо :)</blockquote>
  <p>Поэтому, тут я постараюсь осветить подробно (и с примерами), как можно использовать композицию в ООП вместо наследования, почему наследование это плохо и тому подобные нюансы крутого проггинга 😎.</p>
  <h2>Полиморфизм в Swift</h2>
  <p>По сути, обе концепции (и композиция и наследование) позволяют достичь полиморфизма. Но для начала, давайте вспомним что такое полиморфизм. Существует множество различных видов полиморфизмов, что прекрасно описано, например, на википедии. Я же сосредоточусь скорее на том, что будет являться наиболее важным в контексте Swift.</p>
  <p>Цель полиморфизма состоит в том, чтобы избежать избыточности кода, например дублирования функций, которые принимают разные типы параметров, но делают одно и то же, что делает возможным повторное использование кода.</p>
  <p>Рассмотрим на примере:</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/40/5c/405c37b7-1b69-4599-a586-1d7d592bb2f3.png" width="621" />
    <figcaption>пример с наследованием</figcaption>
  </figure>
  <p>Логика следующая. Допустим есть:</p>
  <ul>
    <li><code>Player</code> - который <code>Solid</code>, <code>Movable</code> и <code>Visible</code></li>
    <li><code>Cloud</code> - который <code>Movable</code> и <code>Visible</code>, но не <code>Solid</code></li>
    <li><code>Building</code> - который <code>Solid</code> и <code>Visible</code>, но не <code>Movable</code></li>
    <li><code>Trap</code> - который <code>Solid</code>, но не <code>Visible</code> и не <code>Movable</code></li>
  </ul>
  <p>И тут мы действительно попадаем в ловушку. В каком-нибудь с++, например, не запрещено наследоваться от множества классов. В Swift множественное наследование классов запрещено (что в целом достаточно логично, если вспомнить о том, что класс – это reference type).</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/5e/7c/5e7cca85-f6d0-43e4-b78d-0b0aa594a2ba.jpeg" width="640" />
    <figcaption>ловушка Inheritance </figcaption>
  </figure>
  <p>Подобное множественное наследование довольно опасно использовать без должного понимания сути проблемы, и обычно оно приводит к тому, что в англоязычном мире зовут <a href="https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem" target="_blank">the diamond problem</a>. Кроме того, даже с идеальным пониманием механизмов наследования, данный код получится, откровенно говоря, довольно дерьмовым. Как же быть? В Swift данная проблема решается с помощью механизма протоколов и расширений.</p>
  <p>Рассмотрим на примере <code>Visible</code>:</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/a3/bb/a3bb1d26-992b-447f-acb7-754fc0428035.png" width="554" />
    <figcaption>protocol-oriented programming</figcaption>
  </figure>
  <p>Ну протоколы протоколами дядя, но как это решает проблему в конечном итоге? Да и к тому же, разве это не чёртова прорва кода? Да, возможно кода стало немного больше, но поверьте мне, это позволит избежать <strong>огромных</strong> проблем в дальнейшем.</p>
  <p>Логика здесь четко разнесена, есть <code>Player</code>, который <code>Visible</code>, <code>Solid</code> и <code>Movable</code>. Всё это интерфейсы, протокол содержит только описательную часть, а сама реализация протокола подвешивается в <code>extension</code>. И в таком случае достаточно написать <code>extension</code> к <code>Visible</code>, содержащий данную конкретную реализацию метода <code>draw</code>. В случае, когда нам понадобится логика <code>Invisible</code>, мы просто добавим этот протокол к конечной структуре/классу. Все просто!</p>
  <p>В случае с <code>Movable</code>, например, всё делается абсолютно аналогично:</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/fa/4d/fa4dd84e-f612-4900-bf74-cae23b66d1bc.png" width="570" />
    <figcaption>extension Movable содержит в себе всю необходимую реализацию поведения Movable</figcaption>
  </figure>
  <blockquote>Далее создаем <code>Building</code> и добавляем в него соответствие протоколу <code>Irremovable</code>. И все. Никаких тебе the diamond problem.</blockquote>
  <p>В с++ данная проблема решается немного иначе, с помощью <a href="https://en.wikipedia.org/wiki/Virtual_inheritance" target="_blank">virtual inheritance</a>. По сути, если не вдаваться в детали, аналогом протоколов там являются pure virtual functions. Хотя протокол это конечно скорее элемент интерфейса, и там ничего виртуального нет.</p>
  <p><code>Player</code> в данном случае будет выглядеть так:</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/ac/b2/acb2f691-fedd-43ba-b7e8-913eeaa7281c.png" width="746" />
    <figcaption>struct здесь потому, что мне неохота было возиться с инициализаторами в классах, но суть от этого не меняется от слова совсем</figcaption>
  </figure>
  <p>Swift не запрещает классу/структуре/перечислению/другому протоколу соответствовать сразу нескольким протоколам. Протоколы, на самом деле хороши тем, что позволяют настраивать гибкое поведение, не наследуя разнообразные болячки от родителей.</p>
  <h2>Выводы</h2>
  <p>При проектировке архитектуры конечного приложения стоит отдавать свое предпочтение композиции, нежели наследованию, поскольку первая более гибкая и легко модифицируется в дальнейшем. Хотя, разумеется, не стоит использовать данный подход всегда и везде.</p>
  <p>С помощью композиции легко изменить поведение на лету с помощью dependency injection. В свою очередь, наследование это более &quot;жесткий&quot; подход, поскольку большинство (нормальных) языков не позволяют наследовать более одного типа.</p>
  <p>Кстати, принцип &quot;композиция вместо наследования&quot; (или принцип &quot;составного повторного использования&quot;) в ООП – это принцип, согласно которому классы должны достигать полиморфного поведения и повторного использования кода посредством их композиции (путем включения экземпляров других классов, реализующих желаемую функциональность), а не путём наследования от базового класса. Это часто упоминаемый принцип ООП, например, в книге <a href="https://en.wikipedia.org/wiki/Design_Patterns" target="_blank">Design Patterns</a> (1994).</p>
  <blockquote>Кстати а вообще причем тут синглтон? Вместо синглтона лучше использовать <a href="https://en.wikipedia.org/wiki/Dependency_injection" target="_blank">dependency injection</a>. Используя композицию вместо наследования, гораздо проще менять поведение &quot;на лету&quot;, используя например всё тот же <a href="https://en.wikipedia.org/wiki/Dependency_injection" target="_blank">dependency injection</a>.</blockquote>
  <hr />
  <p>Полезные ссылки:</p>
  <p><a href="https://medium.com/better-programming/inheritance-vs-composition-2fa0cdd2f939" target="_blank">Inheritance vs Composition (Swift)</a></p>
  <p><a href="https://en.wikipedia.org/wiki/Composition_over_inheritance" target="_blank">Composition over Inheritance (с примерами на с++)</a></p>
  <hr />
  <p>Статья подготовлена для канала <a href="http://t.me/hw_code" target="_blank">Hello World</a>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@hw_code/JahhMPI4m</guid><link>https://teletype.in/@hw_code/JahhMPI4m?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code</link><comments>https://teletype.in/@hw_code/JahhMPI4m?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code#comments</comments><dc:creator>hw_code</dc:creator><title>Почему Swift станет следующим большим языком для Deep Learning</title><pubDate>Sun, 17 Jan 2021 14:59:39 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/8a/a9/8aa94d09-a12d-4341-afa6-2ce11cba2f12.png"></media:content><category>swift</category><description><![CDATA[<img src="https://teletype.in/files/d7/f2/d7f2996a-f7d5-4810-b780-7ee8d850dcc1.png"></img>Если вы занимаетесь deep learning, возможно стоит задуматься о том, чтобы изучить Swift.]]></description><content:encoded><![CDATA[
  <p>Если вы занимаетесь deep learning, возможно стоит задуматься о том, чтобы изучить Swift.</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/d7/f2/d7f2996a-f7d5-4810-b780-7ee8d850dcc1.png" width="700" />
  </figure>
  <blockquote>Данная статья является адаптацией другой статьи (доступной на английском языке), которую я прикреплю в конце.</blockquote>
  <h2>Введение</h2>
  <p>Если вы занимаетесь программированием, то, возможно, когда вы слышите слово &quot;Swift&quot;, то думаете о разработке мобильных приложений для iOS или MacOS. Но если вы занимаетесь deep learning, тогда вы скорее всего слышали о <a href="https://www.tensorflow.org/swift/" target="_blank">Swift для TensorFlow</a> (сокращенно S4TF). Теперь вы спросите себя: &quot;Зачем Google создавать версию TensorFlow для Swift? Ведь есть же версии для Python и C++; зачем добавлять еще один язык?&quot;. В этой статье я постараюсь ответить на эти вопросы, а также обозначить причины, по которым вам следует внимательно следить за S4TF, а также за самим языком Swift.</p>
  <p>Цель этой статьи не в подробных объяснениях, а в предоставлении общего обзора с большим количеством ссылок, чтобы вы могли пойти и копнуть глубже, если вам это интересно.</p>
  <h1>🧠 Swift имеет сильную поддержку</h1>
  <p>Swift был создан <a href="https://en.wikipedia.org/wiki/Chris_Lattner" target="_blank">Крисом Латтнером</a> в то время, когда он работал в Apple. После этого, Крис работал в <a href="https://research.google/teams/brain/" target="_blank">Google Brain</a>, одной из самых лучших исследовательских команд в мире в области искусственного интеллекта. Сам факт того, что создатель языка Swift работал в исследовательской лаборатории, занимающейся deep learning, говорит о том, что это серьезный проект.</p>
  <blockquote>Кстати, Крис еще и в Tesla работал. А с 2020 года занялся разработкой процессоров и микроконтроллеров на архитектуре <a href="https://ru.wikipedia.org/wiki/RISC-V" target="_blank">RISC-V</a>.</blockquote>
  <p>Некоторое время назад, в Google осознали, что хотя Python и является превосходным языком, он также имеет и много ограничений, которые трудно преодолеть. Для TensorFlow был необходим какой-то новый язык, и после долгих обсуждений, Swift был выбран в качестве кандидата. Я не буду вдаваться в подробности, но здесь есть <a href="https://github.com/tensorflow/swift/blob/master/docs/WhySwiftForTensorFlow.md" target="_blank">документ</a>, в котором описаны недостатки Python, и какие другие языки рассматривались в качестве кандидата для TensorFlow, и как в конечном итоге был выбран Swift.</p>
  <h1>💪 Swift для TensorFlow это больше чем просто библиотека</h1>
  <p>Swift для TensoFlow – это не просто «TF для другого языка». По сути, это еще одна ветка (в смысле <a href="https://git-scm.com/book/en/v2" target="_blank">git&#x27;a</a>) самого языка Swift. Это означает, что S4TF не является библиотекой. Это самостоятельный язык со своими фичами, построенный таким образом, чтобы поддерживать ту функциональность, которая необходима для TensorFlow. Например, в S4TF есть мощная система <a href="https://github.com/tensorflow/swift/blob/main/docs/AutomaticDifferentiation.md" target="_blank">автоматического дифференцирования</a>, которая является одной из основ deep learning, необходимая для расчета градиентов. Сравните это с Python, где автоматическое дифференцирование вообще не является частью языка. Некоторые из тех функций, которые изначально разрабатывались как часть S4TF, позже были интегрированы в сам Swift.</p>
  <h1>⚡️Swift быстрый</h1>
  <p>Когда я впервые узнал, что Swift работает так же быстро, как C, я был поражен. Я уже знал, что C хорошо оптимизирован и позволяет достичь очень высокой скорости, но это происходит за счет микро-менеджмента памяти, что приводит к тому, что C не является безопасным, с точки зрения доступа к памяти, языком. Кроме того, C – это точно не тот язык, который очень легко выучить.</p>
  <p>В настоящее время, Swift в численных расчетах работает <a href="https://www.fast.ai/2019/01/10/swift-numerics/" target="_blank">так же быстро как и C</a>, и у него нет проблем с безопасным доступом к памяти, и, что немаловажно, его гораздо проще выучить. А <a href="https://www.infoworld.com/article/3247799/what-is-llvm-the-power-behind-swift-rust-clang-and-more.html" target="_blank">LLVM</a> компилятор (на котором и построен Swift), очень мощный и имеет очень эффективную оптимизацию, гарантирующую, что ваш код будет работать очень быстро.</p>
  <h1>📦 Вы можете использовать библиотеки Python, C и C++ в Swift</h1>
  <p>Поскольку Swift для машинного обучения находится на очень ранней стадии своего существования, это означает, что библиотек машинного обучения для Swift не так много. Но не стоит слишком беспокоиться об этом, потому что Swift обладает потрясающей <a href="https://github.com/tensorflow/swift/blob/main/docs/PythonInteroperability.md" target="_blank">совместимостью с Python</a>. Вы просто импортируете любую библиотеку Python в Swift, и это работает. Точно так же, вы можете <a href="https://oleb.net/blog/2017/12/importing-c-library-into-swift/" target="_blank">импортировать библиотеки C и C++</a> в Swift (только для C++ необходимо убедиться, что хедеры написаны на простом C, без использования C++).</p>
  <p>Подводя итог, если вам нужна конкретная функциональность, но она пока еще не реализована в Swift, вы можете импортировать соответствующий пакет Python, C или C ++. Впечатляет!</p>
  <h1>⚙️ Swift может быть очень низкоуровневым</h1>
  <p>Если вы когда-либо использовали TensorFlow, скорее всего, вы делали это с помощью Python. Под капотом Python-версии библиотеки TensorFlow есть код, написанный на C. Поэтому, когда вы вызываете любую функцию в TensorFlow, на каком-то уровне вы попадаете в код C. Это означает, что существует предел того, насколько глубоко вы можете опуститься, пытаясь проверить исходный код. Например, если вы хотите увидеть, как реализованы свертки, вы не сможете увидеть Python код, потому что эта часть реализована на C.</p>
  <p>В Swift все по-другому. Крис Латтнер назвал Swift «<a href="https://www.fast.ai/2019/03/06/fastai-swift/" target="_blank">синтаксическим сахаром для компилятора LLVM</a>». Это означает, что Swift находится практически на уровне железа, и между ним и TF нет никаких других слоев кода, написанного на C. Это также означает, что Swift работает очень быстро, как это и было описано выше. Все это приводит к тому, что вы, как разработчик, можете проверять код с очень высокого до очень низкого уровня без необходимости углубляться в C.</p>
  <h1>📈 А дальше?</h1>
  <p>Swift – это лишь небольшая часть инноваций в области deep learning, происходящих в Google. Есть еще одна вещь, которая очень тесно с этим связана: MLIR, что означает Multi-Level Intermediate Representation. MLIR будет объединяющей инфраструктурой компилятора Google, позволяющей писать код на Swift (или на любом другом поддерживаемом языке) и компилировать его под любое поддерживаемое оборудование. В настоящее время существует множество компиляторов для различного оборудования, но MLIR изменит это, позволяя не только повторно использовать код, но и писать собственные низкоуровневые компоненты компиляторов. Это также позволит исследователям применять машинное обучение для оптимизации некоторых низкоуровневых алгоритмов.</p>
  <blockquote>Хотя MLIR является компилятором для ML, мы также видим, что он позволяет использовать методы машинного обучения внутри самих компиляторов! Это особенно важно, поскольку инженеры, разрабатывающие численные библиотеки, не масштабируют их с той же скоростью, что и диверсификацию моделей машинного обучения или оборудования.</blockquote>
  <p>Представьте себе возможность использовать deep learning, чтобы помочь оптимизировать алгоритмы низкоуровневой памяти для данных (задача, аналогичная той, что пытается выполнить <a href="https://www.youtube.com/watch?v=3uiEyEKji0M" target="_blank">Halide</a>). Более того, это всего лишь начало, будут и другие, более творческие применения машинного обучения в компиляторах!</p>
  <h2>Подведем итоги</h2>
  <p>Если вы занимаетесь deep learning, то вам, вероятно, стоит начать изучать Swift. Это даст много преимуществ по сравнению с тем же Python. Google вкладывает огромные средства в превращение Swift в ключевую компоненту своей инфраструктуры TensorFlow ML, и весьма вероятно, что Swift станет языком deep learning. Раннее знакомство с языком Swift даст вам некоторое преимущество первопроходца.</p>
  <p><a href="https://towardsdatascience.com/why-swift-may-be-the-next-big-thing-in-deep-learning-f3f6a638ca72" target="_blank">Оригинальная статья на английском</a>.</p>
  <p>От себя также добавлю, что даже если вы и имели некоторый скептицизм в отношении Swift, довольно глупо сосредотачивать все усилия на одном только Python. В настоящее время существует много крутых и действительно значимых для индустрии языков. Всегда старайтесь выбирать <strong>язык под задачу</strong>, <strong>а не</strong> <strong>задачу под язык</strong>. Старайтесь всегда развиваться и двигаться в ногу со временем.</p>
  <hr />
  <p>Статья подготовлена для канала <a href="http://t.me/hw_code" target="_blank">Hello World</a>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@hw_code/SL3hjRvZW</guid><link>https://teletype.in/@hw_code/SL3hjRvZW?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code</link><comments>https://teletype.in/@hw_code/SL3hjRvZW?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code#comments</comments><dc:creator>hw_code</dc:creator><title>dev talks #1</title><pubDate>Thu, 07 Jan 2021 15:14:27 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/9f/c8/9fc88f1a-4ba2-4a6f-8b8e-4d3420924d13.png"></media:content><category>dev talks</category><description><![CDATA[<img src="https://teletype.in/files/0b/10/0b104ee8-7e77-4c6e-abc8-4e3c914e7a86.png"></img>Сегодня мы поговорим о том, как устроена мобильная разработка крупных российских продуктов. У нас в гостях тимлид iOS команды разработки Cardsmobile – Богдан Маншилин.]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/0b/10/0b104ee8-7e77-4c6e-abc8-4e3c914e7a86.png" width="1200" />
  </figure>
  <p>Сегодня мы поговорим о том, как устроена мобильная разработка крупных российских продуктов. У нас в гостях тимлид iOS команды разработки Cardsmobile – Богдан Маншилин.</p>
  <p>Да, все верно, тимлид iOS разработки в Cardsmobile, мы разрабатываем приложение &quot;Кошелёк&quot;.</p>
  <p><em>Давай для начала попробуем разобраться с понятийным аппаратом. Что ты считаешь крупным продуктом, и что – российским?</em></p>
  <p>Хороший вопрос. Я считаю крупным продуктом такой продукт, которым пользуется хотя бы 1.000.000 человек в месяц.</p>
  <p><em>Объясни, почему именно миллион человек в месяц?</em></p>
  <p>Потому что, как мне кажется, эта метрика максимально влияет и на те задачи, которые стоят перед разработчиком, и на те уровни проблем, на которые мы обращаем внимание. Мы можем не обращать внимания на какие-то мелкие баги/крэши в маленьких приложениях, которыми пользуется мало людей. Но если это, допустим, 1% от 1.000.000 пользователей, то это уже большая сумма.</p>
  <p><em>А что по поводу российских продуктов?</em></p>
  <p>Я считаю российскими те продукты, которые были сделаны в России. На примерах, из моего опыта разработки, крупные российские компании это:</p>
  <ul>
    <li>Ultimate Guitar, компания, базирующаяся в Калининграде, то есть полностью вся разработка в Калининграде и head офис в Калининграде, однако продукт в первую очередь используется в США;</li>
    <li>Lamoda и Яндекс.Еда, максимально российские продукты, как по тому, кто их использует, так и по разработчикам;</li>
    <li>Cardsmobile – там, где я сейчас работаю, мы разрабатываем приложение, которое используется в России. Хотя мы сейчас нацелены на Европу, однако когда мы туда выйдем, мы все равно останемся российским продуктом.</li>
  </ul>
  <p><em>Хорошо, вот те приложения, которые ты назвал. Ты над ними работал?</em></p>
  <p>Да, все верно. Работал конечно на разных позициях. В Ultimate Guitar я начинал работать как джуниор-разработчик, и там уже дорос до нормального разработчика. На самом деле сложно говорить о грейдах в Ultimate Guitar, поскольку их там просто не было. По крайней мере на тот момент компания шла по пути &quot;холакратии&quot;, то есть не было никаких лычек &quot;мидл&quot;, &quot;сеньор&quot; или еще кто-то. Вся структура была максимально плоской, за исключением того, что был один руководитель. По своим личным ощущениям, за время работы в Ultimate Guitar я стал хорошим таким мидлом.</p>
  <p>Дальше была Яндекс.Еда, куда я пришел на позицию старшего разработчика, но в какой-то момент я перестал работать над проектом и стал лидом, таким образом перестал писать код вообще. В Ламоде я тоже был немного разработчиком, немного лидом. Сейчас я в принципе не разрабатываю вообще, поскольку мне не хватает времени для того, чтобы писать код. Я руковожу отделом iOS разработки, он у нас довольно большой (сейчас это 10 человек), и мы планируем расти ещё больше.</p>
  <p><em>Расскажи, почему покинул Яндекс.Еду и перешел в Ламоду?</em></p>
  <p>Наверное, отчасти из-за карьерного роста. Хотя, если признаться честно, я пошел за своим руководителем. У нас был руководитель мобильной разработки в Яндекс.Еде, он из Ламоды и по своим причинам вернулся обратно. Какое-то время я пытался развиваться внутри Яндекс.Еды, у меня не получилось. Я не смог понять, какие же у меня там направления развития: бывало так, что я приходил на работу и не знал, чем мне заниматься, и в общем-то от такого отсутствия чётко поставленной цели я уже потянулся за своим прошлым руководителем, с которым мне нравилось работать.</p>
  <h2>Делал приложение под кофейни, где ставят печати на карточку. Как стартапер я прогорел.</h2>
  <p><em>Так, а как ты в Cardsmobile тогда оказался?</em></p>
  <p>О, это очень интересная история. Параллельно с работой я разрабатывал еще и пет-проекты, одним из таких проектов было приложение &quot;кофри&quot;. Идея была такая: я очень хотел сделать приложение, которое полностью бы заменило кошелек, то есть все дисконтные карты перенесло бы из моего кармана в это приложение. Я начал его разрабатывать, но в какой-то момент понял, что это приложение уже существует (это и есть приложение &quot;Кошелек&quot;).</p>
  <p>Я решил не отказываться от идеи, а сделал приложение исключительно под кофейни, наверное, знаешь такое, где печать ставят на карточку. В Кошельке такого не было, и я подумал: отлично, вот открытая ниша, пойду туда. Как стартапер-бизнесмен я прогорел, хотя у нас был готовый продукт, мы достаточно быстро его запилили и работал он хорошо. Однако продать мы его никому не смогли, и я сильно от этого фрустрировал, потому что очень хотелось сделать хороший продукт, но, к сожалению, не получилось.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/46/b6/46b68e67-6043-4ad8-9367-f845630aff81.jpeg" width="360" />
    <figcaption>То самое приложение с кофейнями</figcaption>
  </figure>
  <p>В какой-то момент я увидел на своей почте письмо от HR из Кошелька. О Кошельке я, естественно, уже давно знал, как о своём личном конкуренте. На тот момент я не рассматривал предложения о работе, меня всё устраивало в Ламоде. Но с ребятами я решил пообщаться и узнать, как у них в принципе идёт бизнес. Бизнес у них шел очень интересно: они выходили на европейские рынки, имели стратегического партнера в лице huawei, происходил рост в команде до десятков человек. Собственно, очень большие перспективы, каких не было в Ламоде. Я обсудил это со своим руководителем и понял, что интереснее было бы развиваться вместе с Кошельком.</p>
  <p><em>Расскажи, на чём были написаны все те приложения, с которыми ты работал. Нативная разработка или кросс-платформа? Swift или Objective-C? React-Native или Flutter?</em></p>
  <p>Всего по чуть-чуть. Я всегда был нативным разработчиком и всегда писал либо на Swift, либо на Obj-C. Первое приложение (Ultimate Guitar) было нативным, однако, когда я уходил, стратегия немного поменялась. Кстати, это, наверное, было одной из причин моего ухода – приложения стали кроссплатформенными. И я каюсь, на самом деле я приложил к этому руку. Первый прототип приложения Ultimate Guitar на React-Native разрабатывал я с ещё одним фронтенд-разработчиком, который хорошо разбирался в реакте. Это приложение очень сильно понравилось менеджерам AppStore из раздела Музыка, и в Ultimate Guitar решили разрабатывать всё на React-Native.</p>
  <p>Это был долгий путь, к тому моменту я уже ушёл из компании, ребята набрали команду под реакт, переписали всё приложение, и у них получилось. Было много связанных с этим проблем, но они их решили. Теперь у них приложение полностью на React-Native, но кроме одной части. По сути, Ultimate Guitar – это приложение, которое отображает гитарные табулатуры, аккорды, вот это все. И экран с отображением этих табулатур довольно сложный, на React-Native всё это работало очень медленно. Поэтому ребята решили, что эту часть они напишут... нет, не нативно, а на Qt (Qt – это кроссплатформенная библиотека, написанная на С++).</p>
  <p><em>А что с Ламодой и Яндекс.Едой? Они используют кроссплатформу или что-то нативное?</em></p>
  <p>Оба приложения сейчас нативные. В Ламоде поговаривали о Flutter, но, к счастью, дальше разговоров дело не зашло. Как ты понял, я не большой сторонник кроссплатформенных решений. В Еде, насколько я знаю, какая-то часть (вроде бы Яндекс.Лавка) не нативная, по-моему, она на Flutter, но гарантировать я этого не могу, потому что именно эту часть делали уже после моего ухода.</p>
  <p><em>Видишь ли ты какие-то преимущества нативной разработки над кроссплатформой или наоборот?</em></p>
  <p>В первую очередь это, конечно, UX. Несмотря на то, что какие-то вещи в React-Native или Flutter пытаются сделать нативными (тот же React-Native берет нативные вьюхи и их отрисовывает, но сначала он создает виртуальный интерфейс, поверх которого уже рисуется нативный), у них есть проблемы с реюзабельными коллекциями (TableView), их там не было. То есть все отображённые ячейки висели в памяти приложения, из-за чего оно могло подлагивать.</p>
  <p>Если говорить о Flutter, то, по сути, это технология, которая говорит, что мы самостоятельно перерисовываем весь интерфейс, то есть у нас свой UIKit, который похож на то, как это выглядит нативно. Однако тут мы понимаем, что, если у нас есть какой-то UIKit, который копирует нативное решение, то он копирует либо android либо iOS. То есть есть какая-то общая, базовая часть элементов, но всё таки есть UIKit например называется кажется купертино и обычный, и это два разных кита. И если ты хочешь разработать приложение, которое на iOS не выглядит, как приложение от Гугла, а хочешь, чтобы оно выглядело нативно, то так или иначе тебе всё равно придется пилить два разных интерфейса.</p>
  <p>Однако преимуществ от этого я больших не вижу, поскольку проблемы продолжаются и дальше. Кроме всяких нативных вьюх (которые, по сути, не являются нативными, а только на них отчасти похожи), есть ещё и такие штуки, которые не напишешь на кроссплатформе. Например, виджеты. Сейчас они на SwiftUI. На Flutter их попросту нет. Когда я писал на React-Native, была проблема с отсутствием плагина для force touch. Сейчас, конечно, force touch уже нет, но тогда пришлось самому писать этот плагин, чтобы полностью воспроизвести ту функциональность, которая уже была в приложении. Да и в принципе, даже если кроссплатформа рано или поздно начинает поддерживать новые возможности платформы, она всё равно находится в отстающей позиции. Выходит новая фича на iOS или android, и в кроссплатформе её ещё нет. А если мы хотим всё сделать быстро, ввести эту новую фичу, и чтобы нас зафичерили в AppStore, то кроссплатформа попросту не позволит этого сделать.</p>
  <p><em>Что можешь сказать по поводу скорости разработки?</em></p>
  <p>Это очень тонкий вопрос, мне будет сложно на него ответить однозначно, потому что я больше разрабатывал нативно и только чуть чуть пробовал кроссплатформу в Ultimate Guitar. Но должен сказать, что быстрее у меня разрабатывать на React-Native не вышло... Были какие-то вещи, которые нужно было подпиливать под android. Хотя мы делали приложение только под iOS, мы понимали, что, если будем сейчас делать и под android, то нужно бы сделать ещё вот это, вот это и вот это, дофига всего. И собственно пришлось бы делать второй интерфейс. Это первое. А второе – время от времени ты затыкаешься на том моменте, что кроссплатформа этого не поддерживает. Я частенько вижу реквесты  на Flutter или React-Native, что такой-то фреймворк там не прокинут и не поддерживается до кроссплатформенного sdk, то есть нужно написать плагин, или еще что-то сделать. В общем-то, честно я не могу привести какие то метрики. По ощущениям, сильно быстрее не будет. Конечно, если мы хотим соответствовать качеству нативного приложения.</p>
  <p>Если мы можем подзабить на качество и где-то недокрутить тот же force touch (которого уже нет), ну или виджеты, или забить на то, что где-то вьюшка некрасиво скачет, вот, например, я смотрю сейчас с точки зрения пользователя на Яндекс.Лавку и она выглядит как будто веб-вьюха, вот если мы к этому готовы, тогда на Flutter будет быстрее. Если мы хотим сделать максимально плавно, нативно и красиво, то может оказаться, что кроссплатформенно разрабатывать будет даже дольше.</p>
  <p><em>То есть большие компании всё-таки выбирают именно нативный путь?</em></p>
  <p>Ну, не совсем. Вот та же Яндекс.Лавка – это Flutter. Хотя тоже большая компания. Наверное, тут дело не в том, большая это компания или маленькая, а в том, в каких компаниях или проектах важен UX, а где им можно пренебречь. Яндекс.Лавка считается экспериментальным проектом, и там ребята экспериментируют с Flutter и что с этим получится. Будем ждать статью от Яндекса, где они перешли с Flutter обратно на натив, как в своё время AirBnb перешли с React-Native на нативную разработку.</p>
  <h2>Вечный хакатон vs скрамбан</h2>
  <p><em>Расскажи о том, какие команды работают над всеми этими продуктами? Большие или маленькие? Как устроена структура этих команд и каких процессов они придерживаются?</em></p>
  <p>Довольно обширный вопрос, давай по каждой команде пройдёмся отдельно. Давай возьмем Ultimate Guitar, но я буду говорить о ней образца 2015-2016 года, когда я там работал, поскольку Ultimate Guitar всегда изменяется. Тогда это была команда из 50-60 человек (вообще всех сотрудников), и это была полная холакратия, то есть не было вообще руководителей (кроме одного), и не было процессов. Процесс назывался &quot;вечный хакатон&quot;. Когда ты приходишь на какой-нибудь курс по процессам, тебе говорят, что процесс – это на самом деле препятствие.</p>
  <p>Что мы делаем: у нас есть какая-то задача, мы отдаем ее разработчику, разработчик её делает, не отвлекаясь ни на что, в итоге отдает результат. А все эти ваши скрамы, код ревью, тестирование, это всё разработку замедляет. Идеальный разработчик пишет такой код, который не нуждается ни в код ревью, ни в тестировании, а сразу готов к продакшену. Ultimate Guitar конкретно в то время придерживался именно этого подхода. Никаких лишних процессов не было. Не было ни код ревью, ни тестирования, ни скрамов. К тебе просто приходит какой-нибудь заказчик, то есть, по сути, даже не заказчик, поскольку там заказчиками всех фичей были сами разработчики.</p>
  <p>Процесс был построен таким образом, что когда ты попадаешь в эту команду, твоя задача не только разрабатывать или рисовать дизайны, твоя задача предлагать идеи, питчить их. Ну и, образно говоря, каждый вторник, ну или раз в две недели, сотрудники компании приходили и рассказывали свои идеи. Фаундер смотрит и говорит: это фигня, а это звучит интересно, давайте сделаем. Таким образом, человек, который предложил идею, становится её заказчиком. Он берет эту идею и доводит её до продакшена. Никакого процесса тут нет, он сам находит ресурсы, обращается к разработчикам, сам её планирует. Как на хакатоне, ты придумал идею, собрал несколько человек. Сели и запилили за две ночи, примерно вот так. Очень интересно, но со своими минусами. Человек, который запитчил, должен довести всё до конца сам. Если он не самостоятельный, значит, проект сделан не будет.</p>
  <p>Более классические примеры процессов были в Яндекс.Еде и Ламоде, они были довольно похожие. В Яндекс.Еде iOS разработчиков было примерно 5 штук, а всего в команде – более 100 человек (на всю Яндекс.Еду). Работали мы по методологии скрам, хотя я бы, наверное, назвал это скрамбан.</p>
  <blockquote>Scrumban – это методология, включающая в себя элементы scrum и kanban, однако Богдан называет скрамбаном любую химеру, которую придумывают менеджеры в попытке вобрать лучшее из канбана и скрама.</blockquote>
  <p>Это был недетерминированный процесс. В конечном итоге за основу мы взяли все-таки канбан. У нас была бесконечная борда, где в первой колонке находятся задачи в виде такого бесконечного списка.</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/7e/7b/7e7b17e7-eb63-44b7-a627-316cc59371d3.jpeg" width="1920" />
    <figcaption>Пример скрамбановской борды</figcaption>
  </figure>
  <p>Мы туда вкидывали эти задачи, и они там каким-то образом оценивались. В конечном итоге мы пришли к выводу, что лучше всего для этого подходит грумминг. Но на первых порах это могло быть по-разному, например кто-то из разработчиков, или тимлид самостоятельно оценивал задачу. Мы, собственно, вкидывали всё это в первую колонку, и дальше продакт платформы приоритизировал  задачи. Банально он просто кидал задачу в самый верх колонки. Разработчик, заканчивая заниматься своей задачей, смотрит, что лежит в самом верху ToDo, и забирает задачу. Такой простой процесс.</p>
  <p>Были уже и код ревью нормальные, и QA, причём мы работали по git flow. То есть, когда мы берем какую-то фичу, мы уходим в свою ветку. Пока работаем с фичой, находимся в этой ветке, здесь же был и код ревью, и происходило тестирование. Только потом заливаем это все уже в develop (название ветки). Еще у нас были спринты, ориентированные на проекты. То есть, они были регулярные – раз в 2 недели – но с упором на законченные задачи.</p>
  <p>Обычное ретро, привязанное к спринтам, как происходит? Закрываем спринт, садимся все вместе и говорим, что в спринте было хорошо, а что плохо. В Яндекс.Еде, что интересно, мы поступали не так. Мы брали все задачи, которые к этому моменту успевали завершить (которые уже уехали в релиз), и проходились по ним. Эта задача проехала хорошо, есть по ней комментарии? Если комментариев нет, значит все окей, прилепили стикер на физическую доску и сказали что по ней комментариев нет. Если по задаче есть комментарии (не обязательно плохие), то их обсуждаем, записываем. В идеале, выписываем какие-либо поинты и тд. Это такой, в общем, процесс у нас был в Яндекс.Еде.</p>
  <p>В Ламоде были спринты (то есть не было канбана), эти спринты мы планировали и в дальнейшем оценивали в сторипоинтах, в Еде это было в часах. То есть это был обычный скрам, без всяких интересностей. Всё как по книге. То есть, у нас есть грумминги, планинги, ретро. И все это более-менее работает. Всегда пытались посчитать велосити нашего спринта, чтобы его уметь планировать. Но это обычно не удавалось, и каждый раз мы чуть-чуть выезжали. Сложно было с планированием спринтов, хотя в целом всё работало хорошо. Больших отличий от Еды нет, кроме того, что появились спринты.</p>
  <p>В команде на разных этапах было 7-9 человек, и она была разделена на две части. То есть было две команды по двум продуктовым направлениям. Одни занимаются конвершеном, вторые ретеншеном. Условно это два бизнесовых направления, у каждого есть продакт, за продактом закреплены разработчики (то есть те ресурсы, которые есть у продакта). Таким образом, проект был один, но задачи разные. Работали все в рамках одного проекта. Не было никаких зон ответственности, все отвечали за один проект. Где-то в момент моего ухода из Ламоды эту команду слили обратно в одну.</p>
  <h2>Нам нужно выйти на европейские рынки. Сделайте с этим что-нибудь.</h2>
  <p><em>Что можешь сказать по поводу разработки в Cardsmobile? Это было ближе к хакатонам или к традиционным процессам, таким как скрам, спринты?</em></p>
  <p>По поводу разработки в Cardsmobile. Команд у нас много, в отличие от той же Ламоды, в которой было несколько команд по направлениям. У нас сейчас тоже есть команды по направлениям, как гораздо большие, так и гораздо меньшие (чем в Ламода). У каждой команды есть своя зона ответственности. В каждой из команд есть свои процессы. В общем, конечно, у нас скрам. Допустим, кор команда оценивает задачи в часах, другая команда может оценивать в том, в чём ей нравится, например, в сторипоинтах. Это, собственно, не совсем обычный скрам. Пока что я могу рассказать только про кор команду, потому что в кор команде я являюсь, условно говоря, заказчиком. То есть, у нас нет продакта, как такового, нам прилетают задачи на уровне &quot;нам нужно выйти на европейские рынки, сделайте с этим что-нибудь&quot;. Мы уже тут сидим и придумываем, что же можно сделать, чтобы туда выйти. В связи с этим и процессы строим как нам удобно. Процессы выглядят таким образом: у нас есть спринты, оцениваемся мы в часах. Я, если честно, не вижу разницы между часами и сторипоинтами, даже когда мы оцениваем в часах, я считаю, что это какая-то относительная единица работы. Потому что даже если мы оценили задачу в 4 либо 40 часов, это совершенно не означает, что мы будем её делать 4 или 40 часов. Это просто-напросто подтверждается практикой.</p>
  <blockquote>Все равно наша велосити, в чем бы мы её не считали, в часах или поинтах, это статистически погрешная величина.</blockquote>
  <p>У нас пока нет ретро. Но, возможно, оно будет. Каким оно будет, проектным или ориентированным на спринты, пока непонятно. Мы хотим не просто ввести ретро, потому что, мол, так в скраме написано, а понять, какую цель он решает, чтобы максимально правильным способом его проводить. А возможно, он и не нужен нам вовсе. Как самые яркие тимлиды на конференции Подлодка рассказывали, что ретро не нужны. Конечно, это очень смелый подход, но заставляет задуматься, для чего же они всё-таки нужны.</p>
  <p>У нас есть грумминги. Опять-таки, все эти процессы сейчас скорее находятся на этапе построения во всех наших командах. Говорить о том, как сейчас, наверное, нет большого смысла, потому что через месяц всё будет по-другому. Это единственное, что я могу гарантировать. Я лично над этим работаю.</p>
  <hr />
  <h2>Послесловие</h2>
  <p>У Cardsmobile, кстати, есть <a href="https://t.me/km_engineering" target="_blank">канал</a>. Подписывайтесь, там много всего интересного! А также, в будущем, будет и вторая часть статьи, где мы обсудим, как попасть в крупную техническую компанию, какие у этих компаний проблемы и многое другое.</p>
  <hr />
  <p>Статья подготовлена для канала <a href="https://t.me/hw_code" target="_blank">Hello World</a>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@hw_code/cDFOQ-XK2</guid><link>https://teletype.in/@hw_code/cDFOQ-XK2?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code</link><comments>https://teletype.in/@hw_code/cDFOQ-XK2?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code#comments</comments><dc:creator>hw_code</dc:creator><title>Функторы и монады в Swift</title><pubDate>Wed, 02 Dec 2020 16:53:46 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/b6/42/b64254dd-506a-415c-b83b-b31e01ed59c0.png"></media:content><category>swift</category><description><![CDATA[<img src="https://teletype.in/files/57/ff/57ffc164-940d-47b1-8181-90a2362a15a2.png"></img>Каждый разработчик, копнув чуть дальше в области функционального реактивного программирования, рано или поздно сталкивается с такими понятиями, как функтор или монада. Поначалу они могут показаться довольно страшными, но на самом деле ничего страшного или сложного в них нет.]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/57/ff/57ffc164-940d-47b1-8181-90a2362a15a2.png" width="582" />
    <figcaption>собственно, аппликативный функтор или просто Applicative</figcaption>
  </figure>
  <p>Каждый разработчик, копнув чуть дальше в области функционального реактивного программирования, рано или поздно сталкивается с такими понятиями, как функтор или монада. Поначалу они могут показаться довольно страшными, но на самом деле ничего страшного или сложного в них нет.</p>
  <p>Попробуем разобраться в этих, довольно далеких от императивного программирования, концепциях.</p>
  <blockquote>Если текст покажется вам слишком сложным, вернитесь к нему еще раз после прочтения статьи по хаскелю, которую я приложу в самом конце статьи.</blockquote>
  <h2>Значения в контексте</h2>
  <p>Попробуем сначала разобраться что такое контекст. Допустим у нас есть некоторая простая переменная, например <code>var number = 3</code>. Теперь давайте упакуем это значение в контекст. Представим коробку, в которую мы кладем это значение. Коробка по сути и будет являться простой концепцией контекста.</p>
  <p>Такая коробка может иметь значение, а может и вовсе быть пустой (например как массив значений, или реальная коробка из реального мира). Оперировать с таким значением напрямую мы не можем, поскольку оно упаковано в контексте, но можно попробовать каким-либо образом &quot;распаковать&quot; его, если контейнер не пуст.</p>
  <blockquote>Значение в контексте, или же просто упакованное значение, это одно и то же.</blockquote>
  <p>Внимательный читатель может заметить, что прообразом подобной коробки может являться тип <code>Optional</code> в языке Swift, поскольку <code>Optional</code> может как иметь некоторое значение, так и быть <code>nil</code>. Для наших экспериментов создадим наш собственный <code>Optional</code> в виде дженерика <code>Box&lt;T&gt;</code>, имеющего два кейса:</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/45/8f/458f3424-7da5-42c6-99a6-fd40f1767992.png" width="550" />
    <figcaption>Значение, упакованное в контексте</figcaption>
  </figure>
  <p>Ничего сложного тут нет, даже если вы слабо знакомы с синтаксисом Swift. Перечисления в Swift объявляются в помощью ключевого слова <code>enum</code> и могут иметь различные <em>кейсы перечисления</em>, определяемые с помощью слова <code>case</code>. В данном случае мы можем либо вернуть некоторое значение типа <code>T</code> либо ничего. Переходим к функторам.</p>
  <h2>Функтор</h2>
  <blockquote>Функтор – это такой тип данных, для которого определена функция map.</blockquote>
  <p>Теперь у нас есть значение в контексте (например целое число 7), и мы хотим добавить к нему 3. Но поскольку значение упаковано в контекст, мы не можем просто добавить к нему 3.</p>
  <blockquote>Box(7) + 3 -&gt; Type Error.</blockquote>
  <p>Для того, чтобы выполнить подобное вычисление, нам понадобится функция, выполняющая вычисление в случае если коробка не пуста, и возвращающая &quot;пустое&quot; значение, если коробка пуста.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/28/da/28da81c5-36e7-4836-86b7-444a8806f441.png" width="470" />
    <figcaption>функция add для значений целого типа</figcaption>
  </figure>
  <p>Здесь мы определили функцию <code>add</code>, принимающую целое значение в качестве параметра, выполняющую некоторые вычисления и возвращающую <code>Box</code> типа <code>Int</code>. Давайте перепишем его и сделаем более абстрактным (generic). Здесь на сцену выходит функция <code>map</code>. Такая функция принимает в качестве аргумента другую функцию с сигнатурой (T) -&gt; U, становясь по сути независимой от типа.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/5a/a3/5aa398f6-d3ee-4553-bee5-16f62e856ef3.png" width="566" />
    <figcaption>функция map</figcaption>
  </figure>
  <p><strong>Что мы в итоге имеем:</strong></p>
  <ul>
    <li>функция map принимающая функцию (T) -&gt; U в качестве единственного аргумента и возвращающая <code>Box&lt;U&gt;</code></li>
    <li>если внутри контекста есть значение, мы применяем к нему функцию <code>f</code>, получаем в результате значение типа <code>U</code>, оборачиваем это значение обратно в контекст <code>Box&lt;U&gt;</code> и возвращаем его</li>
    <li>если внутри контекста нет значения, возвращаем <code>empty</code></li>
  </ul>
  <p>Используя функцию <code>map</code> можно применять любую функцию к значению, заключенному в контекст <code>Box</code>.</p>
  <blockquote>Функтор применяет функцию к значению, заключенному в контексте</blockquote>
  <p>Другими словами функтор – это тип данных, для которого определена функция <code>map</code> – функция которая знает как применить другую функцию к значению в контексте.</p>
  <h1>Аппликативный функтор</h1>
  <p>Если пока что все понятно, приступим к аппликативному функтору. Если нет, вернитесь снова к предыдущей главе про функтор, сделайте себе любимый кофе и возвращайтесь к этой главе попозже.</p>
  <p>Итак, мы уже знаем что такое значение в контексте, и как применять к этим значениям функции с помощью функторов. Но допустим, теперь и функция тоже упакована в контекст! И как нам применить эту функцию к значению в контексте? Нужна немного более мощная вещь чем <code>map</code>. Расширим тип <code>Box</code> с помощью функции apply, которая будет переключаться между функцией в контексте и значением.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/2c/78/2c786b20-fc95-4b1d-bc07-57c511271441.png" width="558" />
    <figcaption>функция apply</figcaption>
  </figure>
  <p>Обратим внимание на первый some case. Вложенный <code>switch</code> ничего не напоминает? По сути, это тот же самый <code>map</code>, реализованный чуть выше. Давайте немного отрефакторим код и заменим вложенный <code>switch</code> на <code>map</code>.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/56/f0/56f09539-19da-46e5-9975-94065f91a677.png" width="558" />
    <figcaption>apply реализованный через map</figcaption>
  </figure>
  <p>То есть, <code>apply</code> возвращает некоторое значение, помещенное в контекст только в том случае если есть некоторая функция внутри <code>Box</code>, и некоторое значение внутри <code>Box</code>. В иных случаях, возвращается <code>empty</code>. Теперь определим что такое аппликативный функтор.</p>
  <blockquote>Аппликативный функтор применяет функцию из контекста к значению из контекста.</blockquote>
  <p>Другими словами, аппликативный функтор – это тип, который знает как применить функцию помещенную в контекст, к значению помещенному в контекст.</p>
  <h2>Монада</h2>
  <p>Наконец то. Собственно, это то ради чего мы тут и собрались. Теперь возможно мемы на профункторе станут чуточку понятнее.</p>
  <blockquote>Монада применяет функцию, помещенную в контекст и возвращающую некоторое значение (также помещенное в контекст) к значению из контекста.</blockquote>
  <p>Звучит вообще говоря как-то так себе, правда? Рассмотрим на примере. Определим функцию <code>flatMap</code> для <code>Box</code> и посмотрим как работают монады.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/8a/dc/8adc69ef-5391-4fd9-b182-8f837fcb181a.png" width="654" />
    <figcaption>функция flatMap</figcaption>
  </figure>
  <p>Аргумент функции <code>flatMap</code> возвращает <code>Box&lt;U&gt;</code>, а не просто тип <code>U</code> как например функция <code>apply</code> в аппликативном функторе.</p>
  <blockquote>Монады применяют функцию, которая возвращает упакованное в контекст значение, к упакованному в контекст значению.</blockquote>
  <h2>Заключение</h2>
  <p>Если вы ничего не поняли, не беда. Все таки функциональное программирование вещь не из простых. Чтобы окончательно разобраться в ситуации, рекомендую изучить следующие ссылки:</p>
  <p><a href="https://habr.com/ru/post/183150/" target="_blank">Функторы и монады в картинках</a></p>
  <p><a href="https://habr.com/ru/post/445800/" target="_blank">Монады за 15 минут</a></p>
  <p><a href="http://learnyouahaskell.com/functors-applicative-functors-and-monoids#functors-redux" target="_blank">Англоязычный сайт по хаскелю</a></p>
  <blockquote>Попробовать Swift можно, например <a href="http://online.swiftplayground.run" target="_blank">тут</a>.</blockquote>
  <hr />
  <p>Статья подготовлена и написана для канала <a href="http://t.me/hw_code" target="_blank">Hello World</a>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@hw_code/fvYina6yf</guid><link>https://teletype.in/@hw_code/fvYina6yf?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code</link><comments>https://teletype.in/@hw_code/fvYina6yf?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code#comments</comments><dc:creator>hw_code</dc:creator><title>dev blog #1</title><pubDate>Fri, 18 Sep 2020 16:02:47 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/d2/43/d243591e-34cc-43a9-8d9d-4d2332516eff.png"></media:content><category>mobile app development</category><description><![CDATA[<img src="https://teletype.in/files/d2/43/d243591e-34cc-43a9-8d9d-4d2332516eff.png"></img>В этом выпуске обсудим разработку мобильных приложений, архитектурный паттерн MVVM и многое другое. Выпуск возможно получится несколько длиннее, чем я планировал, но об этом в конце.]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/d2/43/d243591e-34cc-43a9-8d9d-4d2332516eff.png" width="1960" />
  </figure>
  <p>В этом выпуске обсудим разработку мобильных приложений, архитектурный паттерн MVVM и многое другое. Выпуск возможно получится несколько длиннее, чем я планировал, но об этом в конце.</p>
  <p>Эта рубрика создана скорее не с целью научить чему то (хотя и это тоже), но с целью поделиться своим опытом разработки мобильных приложений под конкретную платформу (iOS).</p>
  <blockquote>Все вышесказанное не означает что это не будет полезно тем, кто не пишет на Swift. Напротив, логику можно адаптировать под любую нужную вам платформу.</blockquote>
  <h2>Какие еще аппки, дядя?</h2>
  <p>Что вообще сподвигло меня на написание данной статьи? Телеграм меня утомил. Python я использую по работе и он превратился в рутину. Кроме того, мой мак простаивал, а Swift я хотел освоить еще long long time ago... Ну допустим убедил, но что там по цифрам? Мобильный рынок огромен. Большинство людей, только разлепив глаза ото сна, берут в руки телефон. Про разработчиков всяких китайских игр-фармилок я вообще молчу.</p>
  <p>Точные цифры я не помню, да и ни к чему вам они. Но если вам мало слов, всегда можно проверить примерный заработок того или иного мобильного приложения в <a href="https://sensortower.com" target="_blank">sensor tower</a> (ни разу не реклама, если что). Таким образом, немного изучив конкурентов и рынок в целом, можно влезать в разработку.</p>
  <blockquote>Вообще говоря, мой выбор основывался лишь на моих личных предпочтениях и только (мне просто нравится Swift), но всегда приятно увидеть цифры и заранее положить несуществующие 1-2к $ себе в карман.</blockquote>
  <h2>MVVM</h2>
  <p>Microsoft <a href="https://docs.microsoft.com/en-us/archive/msdn-magazine/2009/february/patterns-wpf-apps-with-the-model-view-viewmodel-design-pattern" target="_blank">презентовала</a> этот шаблон в 2006 году. По сути он является новейшим из всех MV(X) шаблонов, созданный с целью устранения некоторых проблем присущих MV(X). Цель этого шаблона – разделение бизнес логики приложения и его визуальной составляющей. Если слова &quot;бизнес логика&quot; вас не очень то вдохновляют, то забудьте об этом. Речь по сути идет о некоторой абстрактной модели, представляющей всю логику вашего будущего приложения.</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/cf/d0/cfd03245-22f2-481b-a5bc-fbc84208ee94.png" width="2058" />
    <figcaption>Схема распределения зависимостей между View, View Model и Model</figcaption>
  </figure>
  <h2>Model</h2>
  <p>Модель – это абстрактное &quot;нечто&quot;, чаще всего речь идет о дженериках (поскольку они позволяют полностью забить на конкретный тип данных в самой модели). К примеру, если мы хотим реализовать стек, без дженериков нам бы понадобилось сразу указывать тип данных, который мы будем хранить в этом стеке. Вот пример на языке Swift, без использования дженериков:</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/76/81/7681fe05-bbc2-44b4-b063-28f84f368e01.png" width="446" />
    <figcaption>стэк интов</figcaption>
  </figure>
  <p>Здесь все просто: создаем массив интов, передаем элементы в функцию и тд. Строгая типизация не позволяет нам передать сюда <code>Float</code> или <code>String</code>.</p>
  <p>А вот здесь мы передадим абстрактный <code>&lt;Element&gt;</code>:</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/69/b2/69b2eb00-daff-4bb7-9594-a4f28b2757b9.png" width="478" />
    <figcaption>стэк &quot;чего угодно&quot;</figcaption>
  </figure>
  <p>Поэтому дженерики еще называются <em>don&#x27;t care type</em>. Нам абсолютно не важно что передавать, логика остается одна и та же. На самом деле в моделях важны две вещи:</p>
  <ol>
    <li>Модель это <strong>абсолютно абстрактная</strong> вещь</li>
    <li>Модель <strong>не занимается</strong> получением данных и отправкой их в VM или View (о них поговорим чуть позднее), это задача View Model</li>
  </ol>
  <p>По поводу последнего поясню еще раз. Казалось бы, совершенно банальная вещь, допустим нам необходимо получить данные с API или с некоторой БД, и далее передать их для визуализации, или в целях обработки. Получать данные в таком случае будем именно во View Model (VM). Модель не работает с данными напрямую. Мы можем получить данные в VM, а затем передать их внутрь модели с помощью функций, созданных непосредственно для того, чтобы настроить модель нужным образом.</p>
  <p>Это очень тонкий момент. Поскольку модель <strong>абстрактна</strong>, мы не можем (или можем, но не хотим), работать с данными (в контексте вышесказанного) в ней. Приведу еще один пример. Допустим, мы работаем с библиотекой книг. Наша модель будет содержать некоторую структуру, содержащую название книги, автора, и уникальный ID, чтобы удобнее было итерироваться по коллекции. Это все. Далее, во View Model вы создаете массив книг, и получаете данные с внешней API или с внутренней БД.</p>
  <h2>View Model</h2>
  <p>View Model стыкует данные и их непосредственное визуальное представление во View, кроме того обновляет состояние модели. Допустим, пользователь поменял настройки в приложении, следовательно нам нужно:</p>
  <ol>
    <li>Уведомить модель об изменении настроек</li>
    <li>Дать понять View, что уже пора бы отрисовать новый экран</li>
  </ol>
  <p>Главное не забывать о модификаторах доступа. Создавая модель внутри VM, необходимо позаботиться о том, чтобы View не мог непосредственно влиять на саму модель. Например, объявить модель private, а затем настроить функции внутри VM для того, чтобы View имел необходимый уровень доступа к модели. Подобная прослойка в виде VM между Model и View позволяет избежать некоторых неприятных моментов, когда приложение становится достаточно большим.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/42/46/42469071-3c17-44c0-82c0-e5b32d6c0eaa.jpeg" width="320" />
    <figcaption>когда решил использовать MVVM вместо MVC</figcaption>
  </figure>
  <h2>View</h2>
  <p>Что делает View?</p>
  <ol>
    <li>Рисует окна</li>
    <li>Создает и отрисовывает анимации</li>
    <li>Общается с VM</li>
  </ol>
  <p>Здесь важно понимать, что MVVM подразумевает &quot;реактивность&quot; приложений. Это не MVC, где использовался императивный подход &quot;отрисуй мне вот это ВОТ ЗДЕСЬ&quot;, это декларативный подход, где вы описываете какой именно результат вам нужен, а View уже сам как нибудь справляется с этим.</p>
  <p>Реактивность подразумевает собой то, что наше приложение по сути является слушателем изменения состояний у отрисовываемых объектов. То есть, при любом изменении состояния отображаемой переменной (например текст, картинка), мы сразу же отображаем эти изменения. В Swift это называются <code>ObservableObject</code>, и он говорит View об изменении своего состояния, и в свою очередь View понимает что надо отобразить новое состояние.</p>
  <h2>Несколько советов</h2>
  <p>Если вы хотите влиться в разработку приложений, не столь важно android или iOS, для начала позаботьтесь о том, что вы освоили основы требуемого языка программирования, умеете работать с переменными, массивами, циклами, условными конструкциями и тд. Обеспечьте себе <strong>базис</strong>.</p>
  <p>Смотрите лекции про разработку приложений на ютубе, параллельно занимаясь изучением языка. Никаких &quot;я потом погуглю, когда дело дойдет&quot;. Это заведомо неправильный подход. Если что-то не получается, значит вы еще недостаточно опытны, и возвращаетесь к изучению основ. Не надо прыгать выше головы.</p>
  <p>Залог успешного изучения подобных вещей состоит из нескольких шагов. Кратко я это назову условный &quot;цикл разработки&quot;.</p>
  <ol>
    <li>Хорошие лекции по разработке непосредственно приложений (например, <a href="https://www.youtube.com/watch?v=jbtqIBpUG7g&list=RDCMUC-EnprmCZ3OXyAoG7vjVNCA&index=3" target="_blank">Стэнфордские</a>, не знаю есть ли подобные для андроид, но наверняка есть)</li>
    <li>Как минимум одна книга (а лучше две), в которой описываются основы языка</li>
    <li>Вдумчивое чтение документации</li>
    <li>Практика, практика и еще раз практика</li>
  </ol>
  <p>Как только с первыми двумя будет покончено, остается только 3 &lt;--&gt; 4. И так до бесконечности.</p>
  <p>Если вы не знакомы с фичей, которую собираетесь реализовать, не надо копировать код со stackoverflow без понимания его работы. Лучше открыть отдельный проект и потратить лишние 4-6 часов на понимание работы данной конкретной фичи.</p>
  <p>В достаточно сложном проекте, скорее всего, вам придется стыковать эту фичу с многими другими, и без понимания ее работы, скорее всего выйдет откровенно говоря не очень. А если придется менять что-то в дальнейшем? Вся разработка превратится в ад.</p>
  <h2>Тем, кто дочитал</h2>
  <p>Если есть желание, я могу постить больше материала отдельно про Swift. Если да, можете отписать коммент сюда, под статьей. В любом случае, я еще буду писать про создание приложений, моя тупая аппка на подходе и релизнется, надеюсь, в конце сентября - начале октября.</p>
  <blockquote>Это не означает, что я не буду писать про Python. Последний я использую по работе, для визуализации и анализа, и иногда появляется что то интересное. В плане анализа это замечательный язык, но писать на нем приложения глупо, не так ли?</blockquote>
  <hr />
  <p>Я бы так и тянул с этими статьями, но на днях мне написали из <a href="https://habr.com/company/cardsmobile/" target="_blank">Cardsmobile</a>. Оказывается, некоторые разработчики читают мой канал (отдельное спасибо им за это), и у них в планах делиться своим опытом разработки.</p>
  <p>Вот их последняя <a href="https://habr.com/ru/company/cardsmobile/blog/519516/" target="_blank">статья</a> на тему виджетов в iOS14.</p>
  <hr />
  <p>Статья подготовлена и написана для канала <a href="http://t.me/hw_code" target="_blank">Hello World</a> 😉</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@hw_code/tcnVFvhrz</guid><link>https://teletype.in/@hw_code/tcnVFvhrz?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code</link><comments>https://teletype.in/@hw_code/tcnVFvhrz?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code#comments</comments><dc:creator>hw_code</dc:creator><title>Имеет ли смысл 80-символьная длина строки в настоящее время?</title><pubDate>Mon, 10 Aug 2020 14:29:24 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/06/5d/065d7a30-a095-4a08-9429-688a3c82d35a.png"></media:content><description><![CDATA[<img src="https://teletype.in/files/1d/fa/1dfa4b1a-11d6-4b9d-867d-4a1f3c9634b3.jpeg"></img>Одна из самых старых практик кодирования – поддерживать 80-символьную длину строки, и многие из нас следуют ей вслепую, но задумывались ли вы когда-нибудь, почему мы обычно ставим это правило на первое место?]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/1d/fa/1dfa4b1a-11d6-4b9d-867d-4a1f3c9634b3.jpeg" width="1252" />
  </figure>
  <p><em>Одна из самых старых практик кодирования – поддерживать 80-символьную длину строки, и многие из нас следуют ей вслепую, но задумывались ли вы когда-нибудь, почему мы обычно ставим это правило на первое место?</em></p>
  <p>Полагаю, что в эпоху маленьких мониторов это должно было сделать код более удобным для чтения, чтобы все содержимое могло уместиться на одном экране, или, возможно, практика пришла из эпохи перфокарт, которые раньше имели 80-символьную длину. Если учитывать прошлое, вопросов не возникает, но считаете ли вы, что это правило имеет смысл в 2020 году?</p>
  <p>Сегодня мы живем в эпоху, когда у большинства разработчиков большие мониторы, которые могут отображать до 180 символов, не правда ли, это пустая трата драгоценного места на мониторе? На самом деле также происходит излишнее удлинение кода.</p>
  <p>Я впервые узнал о правиле переноса строк после 80 символа, когда читал <a href="http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-136091.html#248" target="_blank">Oracle Code Conventions</a> для языка программирования Java, который в последний раз пересматривался 20 апреля 1999 г.</p>
  <blockquote>4.1 Длина строки<br />Избегайте строк длиной более 80 символов, так как они плохо обрабатываются большинством терминалов и инструментов.<br /><br />Примечание: в примерах для использования в документации должна применяться более короткая длина строки – обычно не более 70 символов.</blockquote>
  <p>Если я правильно понял (могу ошибаться), одна из целей этого правила – однородность. Раньше я считал, что 80 символов – это глупо, но иметь возможность просматривать исходный код, написанный дюжиной разных команд за последние 7 лет, и забыть про необходимость изменять размеры окна – это действительно хорошая вещь. Устойчивая длина строки помогает быстрому чтению кода.</p>
  <p>Поскольку я в основном работал с большими мониторами, такими как 32-дюймовый IPS-монитор LG 32MP58HQ-P с разделителем экрана, я также понимаю, что теряется много драгоценного места. Одинаковая длина строки в 80 символов просто слишком мала. Я лично использую 120, если для проекта, над которым работаю, не была утверждена длина строки, в таком случае я соблюдаю рекомендации.</p>
  <p>Еще одна причина, по которой люди все еще используют 80-символьную длину строки, заключается в том, что они работают с несколькими файлами одновременно, в основном в VIM или в своих IDE, таких как Eclipse или IntelliJ IDEA. Например, если используется стандартная длина строки, можно разместить пару файлов и сравнить их построчно, что, на мой взгляд, является важным преимуществом. Можно даже выполнить трехстороннюю проверку слияния на одном экране без прокрутки в сторону. Кстати, не стоит решать эту проблему за счет лишних переносов.</p>
  <p>Я понимаю, что одинаковая длина строки облегчают анализ и чтение текста, но не важно, речь идет о 80 или 120 символах.</p>
  <p>В качестве заключения хотелось бы отметить, что одинаковая длина хороша, и ее нужно использовать, но 80 или даже 100 символов – слишком мало. Многие разработчики, по-видимому, могли бы работать со 120 или даже 150 символами. Современные широкоэкранные ЖК-мониторы высокой четкости могут легко справиться с большим количеством символов в строке.</p>
  <p>Значительно легче читать более длинные строки, чем строки с лишними переносами, потому что лично мне труднее читать строку с переносом, чем просто видеть все в одной строке. Конечно, это просто мое предпочтение, и не все с ним согласятся.</p>
  <p><a href="https://hackernoon.com/does-column-width-of-80-make-sense-in-2018-50c161fbdcf6" target="_blank">Оригинальная статья</a></p>
  <p>А что думаете вы?</p>
  <hr />
  <p>Статья подготовлена для канала <a href="https://t.me/hw_code" target="_blank">hello world</a>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@hw_code/ldD75Rz6</guid><link>https://teletype.in/@hw_code/ldD75Rz6?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code</link><comments>https://teletype.in/@hw_code/ldD75Rz6?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=hw_code#comments</comments><dc:creator>hw_code</dc:creator><title>Модуль dis и constant folding в Python 3.7</title><pubDate>Mon, 24 Feb 2020 13:43:24 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/a7/82/a782c66f-9036-4b50-9ee6-5c9a554e9d89.png"></media:content><category>python</category><description><![CDATA[<img src="https://teletype.in/files/ae/11/ae117ab0-767d-490f-9f5c-37970d8db98b.png"></img>Недавно я наткнулся на одну интересную вещь. Оказывается, выражение]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/ae/11/ae117ab0-767d-490f-9f5c-37970d8db98b.png" width="2000" />
  </figure>
  <p>Недавно я наткнулся на одну интересную вещь. Оказывается, выражение</p>
  <pre>&gt;&gt;&gt; pow(3,99)</pre>
  <p>работает медленнее, чем вот это:</p>
  <pre>&gt;&gt;&gt; 3**99</pre>
  <p>Эта интересная особенность подтверждается непосредственным вычислением с помощью модуля <strong>timeit</strong>:</p>
  <pre>$ python3 -m timeit &#x27;pow(3,99)&#x27;
500000 loops, best of 5: 527 nsec per loop

$ python3 -m timeit &#x27;3**99&#x27;
500000 loops, best of 5: 460 nsec per loop</pre>
  <p>Разница небольшая, но она все таки есть. Причина, по которой функция pow немного медленнее, заключается в том, что в CPython есть дополнительный шаг загрузки pow из пространства имен. Это также означает, что разница останется более или менее постоянной, даже если мы увеличим входные числа.</p>
  <p>Наша догадка подтвердилась:</p>
  <pre>$ python3 -m timeit &#x27;pow(3,9999)&#x27;
5000 loops, best of 5: 57.6 usec per loop

$ python3 -m timeit &#x27;3**9999&#x27;
5000 loops, best of 5: 58.9 usec per loop</pre>
  <p>В процессе изучения этого вопроса я также пользовался модулем <strong>dis</strong>. Этот модуль позволяет декомпилировать байт-код Python и проверять его.</p>
  <pre>&gt;&gt;&gt; from dis import dis
&gt;&gt;&gt; dis(&#x27;pow(3,99)&#x27;)
  1           0 LOAD_NAME                0 (pow)
              2 LOAD_CONST               0 (3)
              4 LOAD_CONST               1 (99)
              6 CALL_FUNCTION            2
              8 RETURN_VALUE</pre>
  <pre>&gt;&gt;&gt; dis(&#x27;3**99&#x27;)
  1           0 LOAD_CONST               0 (3)
              2 LOAD_CONST               1 (99)
              4 BINARY_POWER
              6 RETURN_VALUE</pre>
  <p>Декомпиляция <strong>pow</strong> в целом имеет смысл. Сначала происходит загрузка <strong>pow</strong> из пространства имен, затем загрузка 3 и 99 в регистры и, наконец, вызов функции <strong>pow</strong>. Но давайте рассмотрим еще один пример:</p>
  <pre>&gt;&gt;&gt; dis(&#x27;3**64&#x27;)
  1           0 LOAD_CONST               0 (3433683820292512484657849089281)
              2 RETURN_VALUE</pre>
  <pre>&gt;&gt;&gt; dis(&#x27;3**65&#x27;)
  1           0 LOAD_CONST               0 (3)
              2 LOAD_CONST               1 (65)
              4 BINARY_POWER
              6 RETURN_VALUE</pre>
  <p>Воу, а вот это уже становится интересным. Почему выводы <strong>dis</strong> двух похожих функций так различаются? Ведь мы всего лишь изменили 64 на 65!</p>
  <p>Этот вопрос относит нас к другой интересной концепции, которая носит название «свертка констант». Это означает, что когда у нас есть константное выражение, Python оценивает значение этого выражения во время компиляции, так что, когда вы запускаете программу, она не занимает много времени, потому что Python использует уже вычисленное значение. Приведу простой пример. Вот это:</p>
  <pre>def one_plue_one():
    return 1+1</pre>
  <p>превращается в это:</p>
  <pre>def one_plue_one():
    return 2</pre>
  <blockquote>Самая распространенная реализация языка (CPython), которую вы обычно используете в своих программах, является интерпретируемой, с некоторой компиляцией. CPython компилирует исходный код на Питоне в байткод, а затем интерпретирует этот байткод, запуская его в процессе.</blockquote>
  <p>Так и почему же constant folding работает для числа 3**64, а не для 3**65? Может быть дело в том, что количество цифр в числе 3**65 больше, а Python ограничивает его например 30 знаками для целого числа? И да и нет.</p>
  <p>Рассмотрим еще один, казалось бы абсурдный, пример:</p>
  <pre>&gt;&gt;&gt; dis(&#x27;2**66&#x27;)
  1           0 LOAD_CONST               0 (2)
              2 LOAD_CONST               1 (66)
              4 BINARY_POWER
              6 RETURN_VALUE</pre>
  <pre>&gt;&gt;&gt; dis(&#x27;4**33&#x27;)
  1           0 LOAD_CONST               2 (73786976294838206464)
              2 RETURN_VALUE</pre>
  <p><strong>Краткий ответ таков:</strong> нет никаких особых правил для constant folding. Есть только детали реализации, заключающиеся в том, что оптимизатор AST включает в себя некоторые меры предосторожности, чтобы не тратить слишком много времени или памяти на constant folding.</p>
  <p>Подобные &quot;меры предосторожности&quot; для 2**66 или 4**33 основаны на количестве битов в LHS и значении RHS:</p>
  <pre>if (PyLong_Check(v) &amp;&amp; PyLong_Check(w) &amp;&amp; Py_SIZE(v) &amp;&amp; Py_SIZE(w) &gt; 0) {
    size_t vbits = _PyLong_NumBits(v);
    size_t wbits = PyLong_AsSize_t(w);
    if (vbits == (size_t)-1 || wbits == (size_t)-1) {
        return NULL;
    }
    if (vbits &gt; MAX_INT_SIZE / wbits) {
        return NULL;
    }
}</pre>
  <p><code>MAX_INT_SIZE</code> определен ранее как 128. Поскольку 2 – это 2-битное число, а 4 – 3-битное число, предполагаемый размер результата меньше для 4**33, поэтому оно проходит проверку и получает constant folding.</p>
  <p>И еще один интересный пример. Поскольку Python производит вычисления слева направо, то выражение <code>1+2+3+x</code> будет оптимизировано с помощью constant folding, а выражение <code>x+3+2+1</code> – нет.</p>
  <pre>&gt;&gt;&gt; dis(&#x27;lambda x: 1+2+3+x&#x27;)
  1           0 LOAD_CONST               0 (&lt;code object &lt;lambda&gt; at 0x10a440c00, file &quot;&lt;dis&gt;&quot;, line 1&gt;)
              2 LOAD_CONST               1 (&#x27;&lt;lambda&gt;&#x27;)
              4 MAKE_FUNCTION            0
              6 RETURN_VALUE</pre>
  <pre>Disassembly of &lt;code object &lt;lambda&gt; at 0x10a440c00, file &quot;&lt;dis&gt;&quot;, line 1&gt;:
  1           0 LOAD_CONST               1 (6)
              2 LOAD_FAST                0 (x)
              4 BINARY_ADD
              6 RETURN_VALUE</pre>
  <p>vs</p>
  <pre>&gt;&gt;&gt; dis(&#x27;lambda x: x+3+2+1&#x27;)
  1           0 LOAD_CONST               0 (&lt;code object &lt;lambda&gt; at 0x10a4400c0, file &quot;&lt;dis&gt;&quot;, line 1&gt;)
              2 LOAD_CONST               1 (&#x27;&lt;lambda&gt;&#x27;)
              4 MAKE_FUNCTION            0
              6 RETURN_VALUE</pre>
  <pre>Disassembly of &lt;code object &lt;lambda&gt; at 0x10a4400c0, file &quot;&lt;dis&gt;&quot;, line 1&gt;:
  1           0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (3)
              4 BINARY_ADD
              6 LOAD_CONST               2 (2)
              8 BINARY_ADD
             10 LOAD_CONST               3 (1)
             12 BINARY_ADD
             14 RETURN_VALUE</pre>
  <hr />
  <blockquote>P.S. Не забывайте делиться статьями и вступать на мой канал <a href="http://t.me/hw_code" target="_blank">hello world</a>. Это поможет выпускать больше качественного материала.</blockquote>

]]></content:encoded></item></channel></rss>