мне кажется, что тут больше говорится о том, что на коленках получаются очень негибкие структуры, и для расширения функционала приходится хорошо попотеть, рефакторизируя код.
Ввод-вывод в Хаскеле может осуществляться не только монадами, но и функторами, апликативными фукнторами, стрелками Клейсли. Было бы желание. Ну и сложность задачи тоже влияет.
Да и монады не так страшны. Основную роль там играет функция bind:
bind :: (a -> m b) -> m a -> m b
что означает, что она берёт 2 аргумента — функцию a -> m b, монадное значение m a и возвращает результат m b
Думаю, что несмотря на название «программируем императивно», линзы ближе к «программируем объектно-ориентированно».
Но, линзы — это всё же не объекты, поэтому говорить о костылях в данном случае неуместно.
В основном человек принимает новое только через что-то знакомое. Поэтому дана попытка показать совершенно новый инструментарий (ранее неизвестный) при помощи аналогий.
Автор предлагает, как я понял, экономить место на плоскости.
Хотя почему-то он не догадывается, что обычные биты(ячейки памяти или регистров) обычно на плоскости — прямоугольники, и тоже плотно упакованы.
Случаем, вы не пересмотрели рекламы, «где маленькие зверьки заворачивают шоколад в фольгу»?
Однако в реальной жизни, шоколад так не делают.
И элементы памяти тоже так не делают.
P.S. Представил себе, как сотни миллионов 6-гранных элементов памяти одновременно по-миллисекундно переключают мостики. ))
Код на Хаскеле несколько отличается от ООП-шного.
И развитие кода тоже имеет несоответствия.
В основном код излишне общ.
И в большинстве случаев достаточно просто использовать код.
При этом нет нужны во всяких фабриках и фабриках фабрик.
Из-за обобщённости нет необходимости изменять поведение, достаточно дописать функционал.
Если и нужно менять, то в основном, добавляется очень малое количество кода — до нескольких строчек.
Хотя конечно же, если код не оптимальный — изменения могут быть существенными.
Необходимо отметить, на Хаскеле данными могут быть не только данными, а и действиями, или совокупностью.
Скрытый текст
Например, монады — это данные, с которыми можно работать в императивном стиле, стрелки — данные, с которыми можно работать как блок-схемами, линзы — данные, с которыми можно работать как с объектами.
Поэтому на Хаскеле не надо отдельно заниматься данными, отдельно действиями, отдельно архитектурой.
Если интересна именно эволюция кода, советую почитать интересную статью автора библиотеки, где он описывает, что он добавил в неё. pipes-3.3.0:…
отрывок
pipes-2.4 впервые идентифицировал существование трёх дополнительных категорий, два из которых я назвал «request» и «respond». Эти категории очень полезны, особенное, если можно использовать ListT с обоими. Но я открыл, что нельзя использовать (/>/) и (\>\) композиции операторов для некоторых прокси-траснформеров, особенно
MaybeP
EitherP
StateP
WriterP
Это очень разочаровывало и казалось действительно странным, что прокси-трансформер может поднять одинаковость этих категорий (т.е. request и respond), но не всегда может поднять соответственную им композицию операторов.
Я принял временное решение, отделить (/>/) и (\>\) в отдельный ListT класс типов.
…
(работая с pipes-directory и ExceptionP обнаружилось), ExceptionP — это всего лишь синоним EitherP, а EitherP не имплементировал ListT класс, а это означало, что я не мог использовать монаду ProduceT. Поэтому я пересмотрел EitherP и открыл, что есть закон, по которому можно сделать инстанс EitherP для ListT, тот, который я искал ранее. Более того, я мог использовать то же самое для применения MaybeP к ListT.
А это означало, что только 2 прокси-трансформера остались без импементирования ListT:
StateP
WriterP
Кроме того, WriterP внутренне определялся под полом через StateP, означая, что если я смогу найти решение по StateP, я смогу объединить ListT назад к Proxy классу.
Попутно, работая с pipes-parse, я открыл несколько неверных частных случаев для StateP, которые давали неверное поведение. Также оказалось, что и WriterP тоже давал неверное поведение в широких вариантах случаев.
Это означало, что я имплементировал оба этих прокси-трансформера неверно, поскольку оба давали много неверных результатов для многих случаев. И оба этих трансформера не могли быть подключены к ListT.
Это наблюдение позволило мне открыть правильное решение: разрешить StateP и WriterP делится эффектами глобально в линии (pipeline), вместо локально.
Это решение фиксировало обе проблемы:
— оба могли имплементировать List и подчинялись законам ListT
— Оба давали правильное поведение во всех частных случаях.
Вследствие этого, я снова объединил ListT к классу Proxy и объединил request и respond к их соответствующим композитным операторам.
Ну, и сейчас все прокси-трансформеры поднимают все четыре категории верно.
Да, это наиболее близко.
Разница заключается в доступе изменения данных.
Интерфейсы дают доступ лишь к прописанным(в интерфейсе) полям, а инстансы — ко всем полям.
Пы.Сы. Кстати, в Хаскеле есть свои Генерики. Даже не один, а 3 направления
— Generic
— Template
— Data
Хаскель не объектно-ориентированный язык.
В нём нет объектов вообще.
Покажу пример 1+3+4:
Функция
mechanic :: Movement m => m ->m
instance Movement (Double,Double) where
move (x, y) = (x + dx, y+dy)
instance Movement FlowerVector where
move (FlowerVector {..}) = FlowerVector {x = x + dx, y = y + dy,
flower_x = flower_x - dx, flower_y = flower_x - dy,
flower = flower}
Можете добавить свои данные. Например, как зависит цвет, форма цветка в зависимости от тех или иных событий.
Можно вообще любые данные обрабатывать, только надо написать экземпляр instance Movement MyData
1)Более простой вариант — объект Механика, принимающий на вход(сеттер) массив пар чисел, и на выходе(геттер) — новые пересчитанный массив пар чисел.
2)Усложнённый вариант Механики, возвращающий несвязанный массив пар чисел.
Объект Вектора с Хризантемкой:
3)Простой вариант — у каждого Вектора своя хризантема.
4)Усложнённый вариант — при этом хризантема во время движения должна крутиться в противофазно, относительно Вектора, к которому прикреплена.
1+3 можно исправить расширенными методами.
2+3, 1+4 лишь в некоторых случаях можно тоже, но так вряд ли кто будет делать. Будут уже править
Думаю правильнее назвать «за наименьшее количество ресурсов», где под ресурсами часто понимают деньги-люди-время — три взаимозависимых параметра.
Да и монады не так страшны. Основную роль там играет функция
bind:что означает, что она берёт 2 аргумента — функцию
a -> m b, монадное значениеm aи возвращает результатm bНо, линзы — это всё же не объекты, поэтому говорить о костылях в данном случае неуместно.
В основном человек принимает новое только через что-то знакомое. Поэтому дана попытка показать совершенно новый инструментарий (ранее неизвестный) при помощи аналогий.
Переводить картинки — это сложно. Спасибо большое за статью!
Вот почти та же самая идея для Cи++, что и классы типов для Хаскеля
Хотя почему-то он не догадывается, что обычные биты(ячейки памяти или регистров) обычно на плоскости — прямоугольники, и тоже плотно упакованы.
Однако в реальной жизни, шоколад так не делают.
И элементы памяти тоже так не делают.
P.S. Представил себе, как сотни миллионов 6-гранных элементов памяти одновременно по-миллисекундно переключают мостики. ))
И развитие кода тоже имеет несоответствия.
В основном код излишне общ.
И в большинстве случаев достаточно просто использовать код.
При этом нет нужны во всяких фабриках и фабриках фабрик.
Из-за обобщённости нет необходимости изменять поведение, достаточно дописать функционал.
Если и нужно менять, то в основном, добавляется очень малое количество кода — до нескольких строчек.
Хотя конечно же, если код не оптимальный — изменения могут быть существенными.
Необходимо отметить, на Хаскеле данными могут быть не только данными, а и действиями, или совокупностью.
Поэтому на Хаскеле не надо отдельно заниматься данными, отдельно действиями, отдельно архитектурой.
Если интересна именно эволюция кода, советую почитать интересную статью автора библиотеки, где он описывает, что он добавил в неё.
pipes-3.3.0:…
MaybeP
EitherP
StateP
WriterP
Это очень разочаровывало и казалось действительно странным, что прокси-трансформер может поднять одинаковость этих категорий (т.е. request и respond), но не всегда может поднять соответственную им композицию операторов.
Я принял временное решение, отделить (/>/) и (\>\) в отдельный ListT класс типов.
…
(работая с pipes-directory и ExceptionP обнаружилось), ExceptionP — это всего лишь синоним EitherP, а EitherP не имплементировал ListT класс, а это означало, что я не мог использовать монаду ProduceT. Поэтому я пересмотрел EitherP и открыл, что есть закон, по которому можно сделать инстанс EitherP для ListT, тот, который я искал ранее. Более того, я мог использовать то же самое для применения MaybeP к ListT.
А это означало, что только 2 прокси-трансформера остались без импементирования ListT:
StateP
WriterP
Кроме того, WriterP внутренне определялся под полом через StateP, означая, что если я смогу найти решение по StateP, я смогу объединить ListT назад к Proxy классу.
Попутно, работая с pipes-parse, я открыл несколько неверных частных случаев для StateP, которые давали неверное поведение. Также оказалось, что и WriterP тоже давал неверное поведение в широких вариантах случаев.
Это означало, что я имплементировал оба этих прокси-трансформера неверно, поскольку оба давали много неверных результатов для многих случаев. И оба этих трансформера не могли быть подключены к ListT.
Это наблюдение позволило мне открыть правильное решение: разрешить StateP и WriterP делится эффектами глобально в линии (pipeline), вместо локально.
Это решение фиксировало обе проблемы:
— оба могли имплементировать List и подчинялись законам ListT
— Оба давали правильное поведение во всех частных случаях.
Вследствие этого, я снова объединил ListT к классу Proxy и объединил request и respond к их соответствующим композитным операторам.
Ну, и сейчас все прокси-трансформеры поднимают все четыре категории верно.
Разница заключается в доступе изменения данных.
Интерфейсы дают доступ лишь к прописанным(в интерфейсе) полям, а инстансы — ко всем полям.
Пы.Сы. Кстати, в Хаскеле есть свои Генерики. Даже не один, а 3 направления
— Generic
— Template
— Data
В нём нет объектов вообще.
Покажу пример 1+3+4:
Функция
Можете добавить свои данные. Например, как зависит цвет, форма цветка в зависимости от тех или иных событий.
Можно вообще любые данные обрабатывать, только надо написать экземпляр
instance Movement MyData1)Более простой вариант — объект Механика, принимающий на вход(сеттер) массив пар чисел, и на выходе(геттер) — новые пересчитанный массив пар чисел.
2)Усложнённый вариант Механики, возвращающий несвязанный массив пар чисел.
Объект Вектора с Хризантемкой:
3)Простой вариант — у каждого Вектора своя хризантема.
4)Усложнённый вариант — при этом хризантема во время движения должна крутиться в противофазно, относительно Вектора, к которому прикреплена.
1+3 можно исправить расширенными методами.
2+3, 1+4 лишь в некоторых случаях можно тоже, но так вряд ли кто будет делать. Будут уже править