xfides

На Пикабу
Дата рождения: 1 января
223 рейтинг 2 подписчика 14 подписок 8 постов 3 в горячем
Награды:
5 лет на Пикабу
4

Замыкание в JS по-человечески

----  ВВЕДЕНИЕ \ ВОДА  ----


Здравствуйте. Сижу в профильных чатах по фронтенду,  и  часто там возникает вопрос у новичков, как понять замыкание. Да, конечно, "малыши", бывает, не читают статьи, ленятся, где-то срезают углы. Но в подобных "уроках" часто используется профессиональный сленг, плюс нужна определенная база по программированию. В общем, информацию-ягодку проходится буквально выдирать из колючек-текстов.


Да, пусть тема избитая, изъезженная. Но, возможно, много кому данная статья поможет. В идеале, надо бы писать на технический ресурс, аля Habr или Medium. Но там надо вычитку делать, официальный деловой тон продумывать, оформление настраивать. А тут... Тут  язык можно попроще, картинку в Paint нарисовал, и все довольны. Однако ягодку я постараюсь сорвать и поднести поближе. Нет, жевать за вас не буду, Но вы хотя бы руки не поцарапаете.


----  ИСХОДНЫЙ \ БАЗОВЫЙ ПРИМЕР  ----


Итак, начнем. Не отходя от кассы, возьмем пример, который сегодня всплыл в чате.

Замыкание в JS по-человечески Javascript, IT, Программирование, Обучение, Длиннопост

Есть 2 функции: outer и inner. Названия довольно описательные. Даже на слух можно понять, где что лежит. После выполнения внешней outer функции её результат (внутренняя inner функция) записывается в переменную innerFrom. Затем полученный результат (внутренняя inner функция) вызывается, и финальный результат записывается в константу resFrom и выводится в консоль.


Вот и все замыкание. Что тут непонятного. Ха-ха. Типа пытался пошутить. Впрочем, ответы в чате в чем-то сильно сходились с моей шуткой. Но нет. Это только вводные данные текущей темы. А дальше...


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


----  ФИЛОСОФСКОЕ ПОНИМАНИЕ ВОПРОСА  ----


Вообразите себе, что вам надо испечь пирог. Но вы не знаете как. И вам мама дает рецепт из своей кулинарной книги. Или вы его распечатали из интернета. Что дальше происходит? Вам надо разобрать текст на бумаге. Потом осмыслить то, что вы прочитали. А потом вам надо действовать. Взяли продукты из холодильника и ... "нарисовали сову \ испекли пирог".


Нам сейчас пирог не так важен. Сконцентрируемся на подготовительных вещах.

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

- осмыслить прочитанный текст \ понять, что это рецепт пирога, а не варенья или самогона \ в уме прикинуть количество шагов для выполнения рецепта ...

- заготовить продукты, чтобы было из чего печь пирог \ подготовить стол для готовки ...


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


----  МОСТИК МЕЖДУ ФИЛОСОФСКОЙ И ТЕХНИЧЕСКОЙ МЫСЛЬЮ  ----


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


Первое, что следует понять, ваш код для компьютера изначально - это просто поток букв. И этот поток букв кода надо разобрать \ просканировать \ определить, что среди этих букв есть слово function, круглые скобочки, фигурные скобочки и т.д.


Дальше распарсенный текст \ код компьютеру надо осмыслить. Забавно звучит. "Компьютер сидит вечером под окном и размышляет. А функции быть или не быть! А может сходить покушать, оперативку себе заполнить, добрее стану..." Осмысление компьютера выглядит как создание специального функционального объекта в оперативной памяти, куда он сложит все свои заметки по распарсенному ранее тексту. Вот здесь начинаются параметры функции. Вот здесь открывается тело функции. Вот здесь закрывается. А имя функции такое-то...


Для вас, новички, эта информация не видна. Она скрыта где-то в дебрях реализации движка JS. А снаружи у себя в коде вы оперируете только тем, что объявили фукнцию и вот - "её нет, она пропала" она есть. Хотите посмотреть, как выглядит этот функциональный объект официально с точки зрения спецификации Ecmascript? У меня даже скриншот с какого-то моего доклада валяется. Сейчас найду.

Замыкание в JS по-человечески Javascript, IT, Программирование, Обучение, Длиннопост

Зацените, как выглядит "осмысление" движком JS вашего кода фукнции, написанной в коде. Красота... Да, это пример другой функции, не из нашего исходного \ базового примера. Но сути дела это не меняет. Вы только всмотритесь. Интерпретатор и параметры определит, и строгий режим, и начало \ конец тела функции. Поймет даже, что функция написана в файле, а не модуле...


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


А дальше рецепт прочитан, осознан. Надо подготовить продукты... Функция, как рецепт - это последовательность действий, которую надо выполнить. В процессе выполнения функция будет оперировать данными \ информацией. То есть функция - это одно, а данные, с которыми функция работает - это совершенно другое. Данные, с которыми работает функция в процессе своего выполнения, лежат в отдельном месте оперативной памяти. Это место даже имеет специальное название - лексическое окружение (Lexical environment).


Почему так назвали - не знаю. Может удачнее было бы назвать local data. Типа местная \ локальная информация. Но я уже привык к Lexical environment (LE). Думаю, и вы запомните. Так что же там за данные валяются? Ответ - все, которые кажутся вам логичными:

- это и объявленные переменные внутри функции

- другие функции, объявленные внутри текущей

- и ваш нелюбимый this

- псевдомассив arguments

- параметры функций


То есть, представьте. Вы вызвали функцию. Пнули осмысленный ранее функциональный объект через специальный метод [[Call]]. Она пошла выполнять написанный вами алгоритм. Если в процессе выполнения ей понадобилась информация - она пошла в свой "чемоданчик LE", дернула тот кусок информации, который нужен - и продолжила свое выполнение. Все просто и логично.


----  БЛИЖЕ К JS \ СВОБОДНЫЕ ПЕРЕМЕННЫЕ  ----


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


Мы почти у цели. Сейчас вот-вот дойдем до замыкания. Но сначала надо задуматься вот над каким моментом. Думаю, уже многие из вас задали следующий правильный вопрос... Вот есть такое понятие как свободные переменные. Это та информация, которая есть \ написана в теле функции, но её нет в чемоданчике LE этой функции. Давайте даже скопирую рисунок сверху сюда, чтобы не листать туда-сюда обратно. Тебе и мне приятно. Загадка из детского журнала "Мурзилки".

Замыкание в JS по-человечески Javascript, IT, Программирование, Обучение, Длиннопост

Обратите внимание на функцию inner c 3 по 6 строки. Если эту функцию вызвать, то как будет выглядеть её "чемоданчик LE" ? Там будет валяться ... а вот не скажу... Проверяю, как вы читали выше материал (this, arguments, num2, res). А вот переменной num1 нет. Она не связана с текущей функцией inner. Переменная num1 свободна словно птица в небесах. И что делать по логике?


Варианты могут быть разные. И эти варианты даже реализованы в разных языках по-разному. Можете погуглить "Проблема фунарга". Но вам это не понравится. Там умные слова, которые мы не очень любим "переваривать". Хотя суть уже вам показана. Так как же поступает JS в том случае, когда идет обращение к переменной, которой нет в LE этой фукнции? Может, падает с ошибкой?


Нет, не падает с ошибкой. Идея проста. Если у ребенка нет своих денег, чтобы купить приставку, он идет и занимает их у мамы. Да, если нет своих данных, мы можем "порыться в чужом чемоданчике LE". Гениально, не правда ли? Как только выбрать среди чужих "чемоданчиков LE" тот, в котором будем искать недостающую информацию?


----  SCOPE CHAIN  КАК РЕШЕНИЕ ПРОБЛЕМЫ СВОБОДНЫХ ПЕРЕМЕННЫХ  ----


Заметили? Что заметили? Как что, а на рисунке видели? Слово новое: scope, область видимости. Знакомый термин, да. Все понимают, однако сформулировать определение для новичка иногда бывает проблемой. Давайте уточним понятия, чтобы мы друг друга хотя бы понимали. А над проблемой отцов и детей поразмышляем позже.


Scope или область видимости - набор информации, с которой работает выполняемый код в текущий момент. Да, вы правы, где-то мы уже об этом говорили. Чемоданчик "LE". Давайте пока договоримся, что будем считать это одним и тем же. За одним важным различием. Scope \ область видимости - философское понятие (информация, с которой работает функция), а техническое выражение этого scope - объект LexicalEnvironment, создаваемый во время исполнения функции под капотом движка JS


Итак, мы хотим, чтобы переменная num1 была в scope \ области видимости функции inner. Однако в чемоданчике LE функции inner этой переменной нет. Мы уже определили вариант решения этой проблемы:  пойти и порыться в чужой области видимости. Или технически перейти в другой объект "чемоданчика LE", не связанный с нашей функцией inner. Переходя по чужим LE, вы фактически ходите по цепи кругом из этих LE. Да! Если не найдем что-то во втором чемоданчике LE, то почему бы не сходить в третий, четвертый...


Набор связанных между собой (специальной ссылкой) лексических окружений LE,  расширяющих область видимости конкретной функции - scope chain. Ух, сложное определение. Каждое слово на вес золота, их надо прям прочувствовать. Хотя идея проста. Рыться в чужих чемоданчиках LE, пока не найдем нужную нам информацию для выполнения нужной нам функции.


----  ЗАМЫКАНИЕ ИЛИ "Как только выбрать среди чужих "чемоданчиков LE" тот, в котором будем искать недостающую информацию?"  ----


Да, мы добрались до заветной главы. Замыкание – механизм, который связывает "чемоданчики LE". Сейчас мы разберем, как работает этот механизм. Но прежде еще немного мыслей по терминологии. Выше написанное определение, которое я еще чуть расширю позже, оно было сформулировано мною. Вы вольны не соглашаться или выразить свое определение. Тут идет игра терминов на пересечении лингвистики и разделов науки, так скажем.

Часто вы можете видеть более упрощенное \ усложненное \ другое определение. Например, возьмем определение с MDN. Замыкание - это комбинация функции и лексического окружения, в котором эта функция была определена. Однако это определение легко спутать, а впоследствии подменить тем же определением scope chain.


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


---- МЕХАНИЗМ ЗАМЫКАНИЯ В JS ----


Скопирую, пожалуй, я рисунок снова.

Замыкание в JS по-человечески Javascript, IT, Программирование, Обучение, Длиннопост

Вот мы все рассматривали функции inner и её проблемы. Теперь обратим внимание на функцию outer на первой строчке. Когда она будет выполняться, что будет лежать в её LE? Да, знаю, вы уже научены. Меня интересует следующее. В "чемоданчике LE" функции outer будет лежать функциональный объект функции inner. Да, функция inner была объявлена в области видимости функции outer, а значит, эта информация будет содержаться в лексическом окружении outer.


Ну раз inner лежит внутри "чемоданчика LE" outer, догадайтесь с трех раз, куда замкнет "чемоданчик LE" функции inner при её вызове? Где будем искать недостающую информацию о переменной num1 при вызове функции inner? Правильно, замыкание свяжет лексическое окружение функции inner c лексическим окружением функции outer. Благодаря чему переменная num1 при выполнении функции inner будет найдена в лексическом окружении функции outer.


Попробую сформулировать по-человечески. Если у ребенка не хватает денег на приставку, будем искать деньги в кошельке у того, где этот ребенок родился. То есть у мамы. Если у мамы денег нет, то будем искать деньги в кошельке... у папы. А вот и нет. Я вас подловил. Ищем недостающее у того, где родились. То есть, если у мамы нет денег ребенку на приставку, пойдем поищем денег у бабушки. Она добрая, любит внука =)


И еще раз, по третьему кругу. Механизм замыкания срабатывает при рождении \ декларации функции. Так как новосозданная функция родилась в родительском лексическом окружении внешней функции, то при будущем вызове новосозданной функции её уже собственное, лексическое окружение будет гарантированно привязано \ замкнуто к родительскому LE внешней функции.


---- МЕХАНИЗМ ЗАМЫКАНИЯ В JS ДЛЯ УПОРНЫХ ----


Взглянем еще раз на рисунок...

Замыкание в JS по-человечески Javascript, IT, Программирование, Обучение, Длиннопост

Посмотрите на созданный функциональный объект Sum. (Осознание текста вашего кода в память компьютера. Помните?) Обратите внимание на одно свойство [[Environment]]. Это свойство заполняется при создании \ декларации функции Sum текущим LexicalEnvironment выполняемого кода. На рисунке это будет LexicalEnvironment всего скрипта ./index.js. (Глобальное лексическое окружение. Там еще глобальный объект window лежит.) То есть, лексическое окружение функции Sum будет замкнуто на глобальное окружение скрипта, потому что эта функция Sum объявлена \ задекларирована в теле глобального скрипта ./index.js.


А вот теперь функцию Sum вызвали. И она для своего выполнения (execution context) готовит свой Sum LE. Правый нижний угол рисунка.

Замыкание в JS по-человечески Javascript, IT, Программирование, Обучение, Длиннопост

То есть, во время вызова функции Sum будет создано её личное лексическое окружение Sum LE.


И ссылка для замыкания Scope будет взята из ранее записанного в функцию Sum свойства [[Environment]].Не путайте ссылку c именем Scope с термином scope как область видимости.


Значением свойства [[Environment]] функционального объекта Sum будет являться ссылка на  внешнее глобальное лексическое окружение  Global LE скрипта index.js.


Потому что при выполнении кода глобального скрипта index.js было встречено и обработано  текстовое объявление \ декларация функции Sum.


Таким образом, после того, как ссылка Scope лексического окружения Sum была скопирована с ссылки [[Environment]] функционального объекта Sum, область видимости функции Sum была расширена глобальной областью видимостью скрипта, образовав scope chain.


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


Да, вы сделали это. Хотя бы прочитали. Надеюсь, даже поняли.


---- ПОДВЕДЕНИЕ ИТОГОВ. НАКАЗАНИЕ ВИНОВНЫХ. НАГРАЖДЕНИЕ НЕПРИЧАСТНЫХ ----


Вернемся к исходному рисунку. Не помню, какой раз его уже копирую...

Замыкание в JS по-человечески Javascript, IT, Программирование, Обучение, Длиннопост

Вопросы для самопроверки:

1. назовите данные лексического окружения функции inner

2. назовите данные лексического окружения функции outer

3. назовите данные глобального лексического окружения

4. скажите, куда замкнуто лексическое окружение функции inner

5. скажите, куда замкнуто лексическое окружение функции outer

6. скажите, куда замкнуто лексическое окружение функции inner (тут подскажу. Оно равно null)

7. почему функция inner не упадет, если в её лексическом окружении нет переменной num1

8. в какой момент исполнения кода создается необходимая информация для замыкания

9.  есть ли в коде еще свободные переменные, кроме переменной num1

10. дайте следующие определения:

---- лексическое окружение \ lexical environment

---- область видимости \ scope

---- цепочка областей видимостей \ scope chain

---- свободная переменная

---- замыкание


Да... смотрю я на это все....и понимаю, что, наверное, я вас обманул, пообещав рассказать по-человечески. Но не сердитесь. Улыбайтесь, господа. Улыбайтесь! 

Показать полностью 7
20

Занятия с напарниками по Haskell (4)

Добрый день. Продолжаю делиться наблюдениями из нашей группы по изучению Haskell (

часть 1, часть 2, часть 3 ). Воскресенье, дел мало. Можно посидеть и посочинять что-то, или рассказать. Тем более давно была предыдущая часть.

(Оффтоп) Прикольно. Только что узнал, что абзацы в тексте в редакторе пикабу можно разделять не только через "enter", но и через специальную кнопку "текст". Подобные блоки текста можно перетаскивать в режиме drag-and-drop.

Занятия у нас продолжаются. Был небольшой перерыв в 2 недели. Один из участников был в отпуске. Он хорошо отдохнул. Природа, свежий воздух, все дела. Вернулся, и все стало по прежнему, но хотя бы не так душно (шутка). 4 занятия в неделю. Читаем книгу, общаемся, решаем задачки.

И начитали мы уже 300 страниц. Из 1200. Достижение =). Да, пусть еще не монады. Но и не сказать , что идем на расслабоне. Книжка хитрая. Идеально держит баланс между "легко заскучать" и "трудно, что легче забросить". За это ей большой респект. Ради интереса попробовал почитать знаменитую "Изучай хаскель во имя добра" - ну не то. Даже несмотря на перевод, картинки, красивое оформление - путь изучения идет через спотыкания. Но это лично мое мнение. Не надо делать на этом глобальные выводы =)

Проходили недавно рекурсию. Ох и злобная тема. Вроде логика простая. Задал базовое условие, и вызываешь фукнцию внутри определения другой. Но как пошли задачки с "переподвыпертами"  - так сразу надо думать. Дополнительные вспомогательные функции писать, совмещать их по типам. Зато сразу научился различать одинарные и двойные кавычки. В одинарных кавычках тип данных - Char, в двойных - [Char], то есть String. Просто на этом моменте чаще всего не сходились типы в задачках на рекурсию.

Сейчас изучаем списки. Забавно автор их подает. Через такие термины как "cons cell" и "spine". Знакомит с основами ленивых вычислений haskell. Например, length списка будет вычислять только хребет списка (spine), не заботясь о том, что лежит в ячейках (cons cell) самого списка. Не заботясь - значит не вычисляя выражение, оставляя его в виде thunk. А само выражение, допускающее внутри себя thunk-и, находится в weak head normal form

( https://wiki.haskell.org/Weak_head_normal_form ). Читатель скажет: "фигня какая-то". А это только введение =)

Ладно уж. Учимся и страдаем. Страдаем и учимся. Хорошо, что все-таки вместе, в группе. Потому что в одиночку неприязнь к подобным вещам наступает быстро. А так, не хочется сильно тупить на созвоне - так даже иногда заранее читаешь наперед.

Прикол даже появился. Как-то попалась нам задачка вычислить 10-милионный палиндром на целых числах. Не спрашивайте, откуда задачка взялась. Кто-то пришел, ноги с улицы не вытер, и занес нам это. Так эта задачка сильно раззадорила участника нашей группы. И чтобы её решить - он походу наперед весь хаскель уже выучил. И оптимизации, и массивы, и фиг еще знает что. А я что - а я дождусь, когда он её решит, и я поинтересуюсь решением. Да, такой вот я нахлебник в этом вопросе =)

Автор упомянул функторы вскользь. Ах. Прям запахло приближением монад. Пока не совсем понятно. Абстракция над преобразованием абстрактного структурированного типа. Знакомиться подробно будем позже. Но радует, что я все ближе и ближе к своей цели по пониманию монад.

Вот такие дела. Пора выводы делать. А они простые. Хорошо, что с пользой трачу время своего сна на haskell, хоть это и не всегда приятно по ощущениям. Радует, что что-то остается в голове. Стремлюсь к большему. Чего и вам, читатели, желаю =)

Показать полностью
7

Вопрос по composer php. Помогите

Привет всем. Знаю, что спрашиваю не на совсем профильном ресурсе. Но вдруг мне повезет. И мне подскажут ответ, над которым я бьюсь уже неделю.

Краткая предыстория вопроса. Все началось с того, что мы с другом изучали Laravel. И все шло хорошо, пока чей-то цепкий взгляд не заметил одну удивительную для нас штуку. Оказывается, не все зависимости, которые использует Laravel установлены в папку vendor пакетного менеджера composer. Я уже даже пожалел, что кто-то на совместных занятиях у нас такой зоркий.


Начали выяснять, как Laravel работает , если установлены не все зависимости. Выяснили что зависимости-то как раз есть все. Просто часть из них скрыта в исходном коде фреймворка под директивой "replace" composer.json схемы (ссылка -> https://getcomposer.org/doc/04-schema.md#replace ). Мы кое-как вникли в смысл этой директивы. Но остался небольшой нюанс.

Смысл директивы "replace", как мы ... эээ смогли понять. Разработчик продукта имеет право воспользоваться готовым пакетом не как зависимостью (через require), а скопировать  \ форкнуть код пакета напрямую в свой разрабатываемый код. Зачем так вообще делать - отдельный вопрос. Мы не особо понимаем. Но раз так делают, значит бывает нужно.


Директива "replace" приказывает пакетному менеджеру composer не выкачивать пакет из удаленного репозитория, а просто запоминает, что этот пакет уже выкачан самим разработчиком. Естественно, разработчик в своем коде должен предусмотреть не тупое копирование кода, а все-таки правильное подключение скопированного кода. Ну то есть, например, хотя бы подключить namespace скачанного пакета в автозагрузку.


Теперь сам вопрос. В директиве "replace" мы указываем название пакета, над которым перехватываем контроль вместо composer. Но также нужно указать версию заменяемого пакета. И практически везде вместе определенной версии стоит указание "self.version"

"replace": {

"illuminate/auth": "self.version",

...

}


Что означает такое ограничение версии как "self.version"? Как composer будет определять единственную конечную версию пакета, если какой-то 3-party пакет запросит тот же illuminate/auth, но вполне определенной версии (см пример ниже)?


3-party пакет разрабатываемого проекта, подключенный как обычная зависимость через require, внутри себя требует ...

"require": {

"illuminate/auth": "8.83.1",

...

}


Google, к сожалению, ничем не помогает. Мы сейчас разбираем исходный код composer. Но там довольно глубокая кроличья нора. Мы уже готовы признать, что это тупик, и нам не найти ответа на этот вопрос. Помогите выиграть нам в этом моменте, не хочется сдаваться.

Показать полностью
12

Занятия с напарниками по Haskell (3)

Добрый вечер. Продолжаю делиться наблюдениями из нашей группы по изучению  Haskell (
часть 1часть 2  ). Скорее всего, пишу текст для себя, попутно повторяя \ вспоминая пройденный материал. Итак, поехали.

Прошли базовые функции для списков: head, tail, take, drop, !! . Отдельная благодарность автору книги в этом моменте, что он не стал подробно расписывать, какие же крутые это вещи, а просто обозначил их как API. Берите, пользуйтесь. Вы же не глупые: сможете различить, что значит взять голову списка, а что значит взять первые 5 элементов. Так что эту тему проскочили быстро.


А дальше начались базовые определения даже не столько языка Haskell, а  программирования в целом. Type, scope, local bindings, data structure, list... Хорошо, что у каждого из нас есть какой-никакой программистский бэкграунд. Поэтому именно эти определения у нас не вызвали затруднения. А вот следующие термины уже нас напрягли.


Data declaration (0), type constructor (1), data constructor (2). Вот в наших головах слово "конструктор" связано с  тем, что это функция, которая должна что-то создать. Конструктор может принимать аргументы. А когда мы пишем (0) data (1) Bool = (2) False | (2) True - тут вроде как ничего не создается в прошлом, привычном для нас понимании. Ну ок, допустим, что мы ввели новую сущность тип Bool. Да, он появился на свет, типа  создался. Но False, True - почему  их называем конструкторами? Это же значения, это данные, которые "населяют" наш Bool. Их не вызывают. Бесспорно , какие-то догадки есть. Кто-то наперед заглянул, кто-то гугл помучил. Но все равно очень непривычно. В этом моменте впервые столкнулись с тем, что наш предыдущий опыт скорее даже мешал, чем помогал в осознании текста книги.

Затем автор ввел понятие переменной типа (type variable). Заставил нас различать оператор ++ и функцию concat. Заставил прочувствовать разницу следующих сигнатур: 1) [a]->[a]->[a] и 2) [[a]] -> [a]. Признаюсь,  вроде бы такой простой пример, но у меня с ним трудности. До сих пор спотыкаюсь на различии этих видов конкатенации. И там, и тут возвращаются списки. А как они получились - мне не важно. Может, как станет важно, так и все станет легко и прозрачно.


Дальше нас начали знакомить с базовыми типами. Научились отличать смысл следующих слов: Integral, Int, Integer. Через пару страниц посмеялись, что, оказывается, в комплект входят Int8, Int16... Все познается в сравнении. JS, твой универсальный double на все времена по сравнению с зоопарком в Haskell кажется таким простым. А Integer от Haskell удивительно похож по своей задумке на BigInt в JS. Вот интересно,  а как можно считать бесконечно большие числа на компьютере, если не через биты памяти того же стандарта IEE-754? Переводить в строки, строить отдельный парсер... Кто знает, напиши в комментариях, в чем там фокус?

В итоге, мы научились чувствовать, что в Haskell есть отдельный уровень типизации, а есть отдельный уровень вычислений. Путать их нехорошо. Их надо держать под своим контролем в связке и согласованности. Еще попутно автор намекал, что нас ждут Type classes. Просил не расстраиваться. Сказал, что пока думайте о них как об интерфейсах в классических языках программирования про ООП. А потом, когда дойдем до этой темы, там все нам объяснит. Брр. Опять отсылка в будущее. Бесит.

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

Показать полностью
15

Занятия с напарниками по Haskell (2)

Здравствуйте. Продолжаю описывать интересные моменты из наших занятий в группе по Haskell ( Предыдущий пост ). 


Прошла еще неделя. Мы стабильно выдерживаем 1ч 20мин за занятие в день. Иногда чуть подольше.

Книжка тупиков пока нам не ставит. Но после первой главы восторженное впечатление от книги подупало. Это связано скорее всего с 2 вещами. Первое, это неравномерность подачи материала в плане трудности. То есть пошли места, где все очень легко. Ты расслабляешься, читаешь в быстром темпе страницу, другую, а потом  раз - и буксуешь абзац за абзацем.


Второе - о, это особенный момент, который встречается много в каких обучающих материалах!

Этот неприятный момент -  прыжки в будущее. Можно читать, разбирать базовые операции со строками, но уже в примерах используются модули и\или монада IO. Причем автор книги уверяет читателя в том, что вы узнаете и поймете эти моменты потом. Сейчас же просто скопируйте пример кода и запустите. Сместите фокус на то, что делает функция putStrLn, а на тип не смотрите. Потом узнаете зачем в типе у этой функции написано IO.


Знаете, вот когда постигал основы JS в свое время, часто натыкался на подобное. Вот внешняя функция  возвращает внутреннюю функцию, а переменные внешней будут жить и использоваться внутренней благодаря замыканию. Да вы это обязательно поймете. Будете писать код, примеры, упражнения, и все встанет на свои места. А пока изучим следующую тему.


Теряется последовательность изложения материала. А это сбивает. И если мозг можно усмирить - реально сместить фокус на важное, то на эмоциональном уровне остаются мелкие "зазубрины". Эх, вот бы еще и то знать. А как вон ту штуку использовать, про которую рассказали? Блин, ну интересно же было бы понять. А прыгать вперед еще хуже. Это и с мысли собьет. И все равно это будет непонятно.


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


В начале занятий иногда проводим опросы, вдруг кто что подзабыл. Был случай: дали напарнику пример из книги на подстановки из лямбда-исчисления, применение термов. И пример был решен. Вроде даже хорошо. Только забыли про альфа-эквивалентность. И следовательно, ответ был не верен. Ничего страшного. Другой пример уже был сделан как надо.


Начали делать первые практические упражнения. Тут наши предпочтения разошлись. Моя личная цель - это изучения языка, а не его экосистемы. Поэтому я просто открываю вкладку с онлайн компилятором Haskell и тут же решаю задачи. Ребята же захотели установить среду разработки себе на компьютер локально. GhcUP или как-то так. Там еще stack, cabal. Брр, мне пока лень в этом разбираться. Наверное, напарники поступают лучше. Может, в будущем они меня мотивируют поступить так же.

Ну и поделюсь еще одним интересным случаем. Разбирали мы простейшие математические операторы в Haskell. Умножение, сложение, вычитание. И div, mod, quot, rem. Какой остаток будет от деления 20 на 6? Два - бодро отвечает вся группа. А каков будет остаток от деления 20 на -6? И все, мы поплыли. Думали, сейчас в гугле посмотрим, за 5 минут разберемся. 3 парням, которым за 30, и не справиться с таким простейшим вопросом? Ага. Не справились, ни за 5 минут, ни за оставшиеся полчаса того занятия. Справились за выходные к следующему уроку. И то, на троих. Первый разобрался, что такое делимое, делитель, неполное частное, остаток от деления. Второй указал на момент, что бывает округление в минус бесконечность и к нулю. Третий собрал эти факты в кучу, и наконец-то - вы можете нас поздравить с пониманием куска школьной программы.


Посмотрим, что будет дальше. Будут новые главы книги, будут новые вызовы. Справимся? =) 

Показать полностью
42

Занятия с напарниками по Haskell

Собирал я как-то напарников в группу для изучения haskell. Пост вот написал на пикабу ( Ищу напарника для совместного изучения haskell ). Сейчас есть немного времени. Расскажу, что происходило дальше. Вдруг кому интересно будет. Или для себя напишу, типа как дневник.


Откликнулось 2 человека. Один из дискорда, другой с пикабу. Всем нам что-то около 32-35 лет. У каждого своя судьба, но все вместе сошлись в своих стремлениях. Haskell нам интересен, но нужен мотивирующе-дисциплинирующий фактор. И группа с совместными занятиями - хорошее решение.


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


Группа выручает. Часто бывает, что где-то один подвисает. Другие объясняют, подсказывают. Например, я затупил над выражением "Project building tools". Спрашиваю - это что такое, npm что ли? Говорят нет - это аля Gulp, Grunt. Кому-то немножко трудно было разобраться с применением абстракции в лямбда исчислении. По сути для себя сделали вывод, что это особая постановка. Заключается в три этапа, связывание параметра с аргументом, вычисления тела абстракции, и отрезания головы функции. Слово английское еще такое забавное выучил: eliminating. Потренировались в этом приеме - стало куда яснее.


Забавный случай вспомнился. Изучили определение ссылочной прозрачности. Для выполнения этого условия необходимо, чтобы функция была чистой. И кто-то по-приколу спросил, а если функция чистая, означает ли это, что она автоматически ссылочно-прозрачная. А в книжке то прямого ответа на этот вопрос нет. Погуглили, подумали. Ничего внятного не нашли. Сделали вывод. Пока не найдем опровержения, приравняем эти 2 термина друг к другу. Типа чистая функция ссылочно-прозрачная и наоборот.


Не знаю даже, что в подобных постах  описывать. Дневники особо никогда не вел. Непривычно, стремно. Думаешь, вода водой. Может, стоит описывать конкретные результаты занятий. Типа конспект, что выучил, какие определения надо запомнить, какие алгоритмы разобрали. Словарь терминов завести? А может, просто делиться впечатлениями, затупами. Историями о том, как мы героически решали задачу. Посмотрим. Может вообще свободное время и желание только  сейчас появилось для поста на пикабу, а потому будет некогда. Посмотрим.

Можно было бы даже  проводить вечера на ютубе. То есть мы проводим занятие. И оно транслируется для всех других. И весь мир будет учить haskell вместе с нами. =) Но это так, утопия.


В любом случае учеба продолжается. Мы хотим стать чуточку умнее, реально подразобраться в функциональной парадигме. Посмотрим, насколько нас хватит =)

Показать полностью

Ищу напарника для совместного изучения haskell (Дубль)

Если можно \ возможно то данный пост надо удалить. Так как он изначально он был создан по невнимательности автора. Данная запись - дубль вот этого поста


Ищу напарника для совместного изучения haskell

3

Ищу напарника для совместного изучения haskell

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


Коротко о главном. Ищу напарника для изучения haskell. Чтобы совместные занятия были эффективными, необходимо:

- быть готовым заниматься от 4 дней в неделю от 1.5 часов ежедневно. Мы определим расписание и будем его придерживаться;

- быть готовым не “на недельку попробовать” , а прозаниматься от 2 месяцев. Вода камень точит, но для результатов нужно ощутимое время;

- иметь хоть какой-то бекграунд в программировании. Чтобы слова “абстракция, функция, параметр...” не ставили вас в тупик;

- не стесняться своих мыслей, уметь их формулировать, не просто сидеть молча и слушать, а быть заинтересованным, вовлеченным;

- так как я сам недавно только начал изучать haskell, то для синхронизации удачно будет вам тоже находиться приблизительно на этом же уровне;

- для связи писать в телеграмм ( @xfides );


Распространенные вопросы \ мнения:


Вопрос - Это платно? И кто вообще кому должен платить. Я напарнику или напарник мне?

Ответ - Нет, тут никто никому ничего не должен платить. Мы одного уровня, у нас общие цели. В идеале.


Вопрос - Может, легче взять ментора?

Ответ - Может и легче. Но я не находил бесплатных менторов, готовых заниматься со мной именно в таком режиме, по такой схеме. Хотя и пытался.


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

Ответ - Спасибо за советы. Но в данный момент я буду заниматься по вышеописанной схеме. Таков мой выбор.


Вопрос - Ты пробовал раньше так делать \ заниматься? Это вообще работает?

Ответ - Да. Не только пробовал, а до сих пор по многим вещам так учусь. Это работает.


Вопрос - Ну допустим, а как будут проходить занятия?

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


Вопрос - Собираться вместе онлайн, чтобы читать книги? Ты прикалываешься?

Ответ - Нет.


Вопрос - Так я и один могу сам читать. В чем плюсы совместного чтения?

Ответ - Мы не будем позволять друг другу “срезать углы” на сложных моментах. Мы будем задавать вопросы друг другу по прочитанному абзацу, проверяя \ выясняя, а совпадает ли у нас понимание пройденного материала. Если не совпадает, мы останавливаемся и ... чаще всего перечитываем абзац. Реже идем за дополнительным источником информации в интернет. Совместное прохождение материала в 2-3 раза медленнее чем в одиночку, но в 1,5-2 раза качественнее осознание. Плюс дисциплина нашей группы не даст прохалтурить в течении длительного времени.


Вопрос - А зачем сюда пишешь? Вот есть профильные паблики и форумы. Иди туда и пиши.

Ответ - Уже туда писал. Пока оттуда никто не откликается. Вообще в целом, я заметил, что поиск человека по духу для совместных занятий занимает от месяца до двух. Поэтому я не спешу. И рассматриваю различные варианты достижения своих целей.


Вопрос - Если бы JS \ React \ Laravel \ подставьте что-то свое - то я бы подумал. А haskell...

Ответ - JS уже знаю. React в планах есть, но немного позже. А меня сейчас интересует именно haskell. И еще приятная новость. Для того, чтобы изучать любимую \ необходимую технологию, то не надо ждать. Создавайте уже свои мини-группы с подобным укладом по своим любимым темам и вперед, к покорению звезд.


Вопрос - Постой, мини-группы? Ты же сказал, что ищешь напарника.

Ответ - Да. Я заметил, что эффективная группа состоит из 3 человек. Из 2 , только с 1 напарником, чуть хуже эффективность. Из 4 приемлимо. Больше 4 человек - плохо. Лучше разделиться еще на подгруппы. Почему сейчас говорю “напарник” - потому что хочу найти хотя бы 1 человека на совместные занятия.


Вопрос - Я вот тут еще кое-что хотел спросить...

Ответ - Спрашивайте в комментариях. Постараюсь дать ответ.

Показать полностью
Отличная работа, все прочитано!