• Философия нуля
    +2

    Обожаю эту могучую "реальность", в которой всё всегда не так, как на самом деле :) Если бы реальность сопротивлялась моделированию с помощью математики, мы бы строили иную математику. Пока всё, имеющее смысл, сходится. Споры с привлечением "реальности", чаще всего, возникают вокруг утверждений смысла не имеющих.

  • RESHI.RU — робот решает и объясняет школьные текстовые задачи по математике
    0

    Таких конструктивных вопросов было два. Из 42 комментов про "пользу" и "несовершенство" :)

  • RESHI.RU — робот решает и объясняет школьные текстовые задачи по математике
    +3

    Комментарии напомнили знаменитый анекдот про японскую лесопилку и суровых сибирских мужиков.
    Интереснейшая задача, человек вплотную подошел к её реальному решению, рассказал как он это сделал и полностью открыт для общения со спецами, но! Самым интересным для уважаемых спецов оказалось тыкать в робота палкой, до тех пор, пока он не сломается, а потом гордо сказать: "ага!!" :)
    Поздравляю автора с отличной задачей и классной реализацией! Уверен, что вы с сыном получили то, что искали: и пользу и удовольствие. А сын ещё и получил возможность восхититься тем, на что способны математика и папа!
    В то время, как тыкатели палками могут гордиться тем, что они могут сломать что угодно, доказывая тем самым превосходство человека "над системой" :)

  • Философия деления на… или исповедь сумасшедшего
    +4

    К сожалению, автор попадает в ловушку "убийства дракона": пытаясь победить формализм исходя из соображений "реальности" и "практической полезности", он теперь вынужден создать новый формализм, причем по всем правилам. Без четкой аксиоматики, без доказательства того, что новая система является полем, использовать её наряду с числами нельзя. Кроме того, похоже, что в этой системе есть нетривиальные делители нуля, а значит, она полем не является, значит неизбежны проблемы с единственностью разложения на множители и сокращениями при делении. Самое же главное, дракон, с которым сражается автор, столь же нереален и далёк от практики, как и указанная в самом начале "проблема формальной математики". Даже сугубо реалистичной инженерной физике никогда не мешали ограничения, накладываемые на операции с числами теорией полей, а, напротив, помогали познавать и моделировать сущности. Анализ размерности — прекрасный инструмент инженера, теория групп — инструмент теорфизика, теория конечных полей, типов и категорий — инструмент программиста. Возможно, предлагаемый здесь формализм может стать одним из таких инструментов, но пока я вижу в нём больше противоречий (указанных уже комментаторами выше), чем может позволить себе теория.
    К слову, если статья на математическую тему начинается с упоминания теоремы Гёделя о полноте, это сильный признак её нематематичности. А если она, к тому же, переключается на вопросы "практичности" математики, то материал, скорее всего, не интересен и не полезен.
    Лучше оставить в покое Гёделя, и построить стройную непротиворечивую аксиоматику, с выводом основных положений, типа теоремы Безу, основной теоремы арифметики и т.п. А если уж так хочется "полезности", показать пример приложения новой системы за пределами возможностей уже известных.

  • Ненаучно о монадах
    0

    И именно это написано в сигнатуре функции bind (flatMap):
    bind :: Monad m => m a -> (a -> m b) -> m b
    И почему говорят, что Хаскелл это сложно? :)

  • Ненаучно о монадах
    +2

    И тут вдруг, кто-то говорит, что функция (функциональная стрелка), тоже образует монаду, а приличный класс Set, реализующий множество уникальных элементов — не образует, хотя неполиморфные методы map и flatMap написать для него можно.
    Монады, действительно, не что-то сложное, но что-то важное, и это не только класс с такими методами. Я поддерживаю автора статьи в том, что для программистов, монады это абстракция вычислений, как композиции.

  • Мифы и реальность ООП
    0

    Конечность, строго говоря, не обязательна: \ dev\null тому пример. И вы правильно определили основные структуры, присущие файлу, как математическому объекту.

  • Мифы и реальность ООП
    0

    Это называется проблема поиска денотационной семантики. Работа в этом направлении идёт и далека от завершения. Для многих это жуть жуткая и муть мутная, каждый день не нужная. Но для тех кто пишет компиляторы и верификаторы это крутой инструмент.
    А файл (POSIX) можно отождествить с анаморфизмом, порождающим свободный моноид, его обработчики — с гиломорфизмами. Файловая система — дерево, или более общий граф, если она реляционная и т.п. Мы используем эти понятия, не зная, что "говорим прозой".


    И хотел бы добавить, современная математика огромна. Не торопитесь судить о ней в терминах "никакая математика не определит"… Например, о трех табуретках: теория устойчивости, даже линейная удовлетворительно отличит одно применение от другого.

  • Про ООП
    0

    Ну, скучно же, ребята!!! Один пишет про ошибки, допущенные другим в статье про ошибки третьих… спор о том, кто больше неправ продолжился в комментариях. Ну неправ, и что? Лучше бы написали о каком-нибудь крутом и красивом решении, которое показывает как XXX парадигма расширяет наш инструментарий, да с примерами, да так чтобы захотелось попробовать! А то "тут ООП, тут не ООП..." Как говорил Жванецкий: "Воруйте с прибылей!"

  • Хватит спорить про функциональное программирование и ООП
    0

    Ну да, но их же нужно откуда-то брать. И при этом не забивать память гигами нулей и единиц, используя решето, и не тратить на это даже секунды. Я далёк от теории чисел, но понимаю, что есть более эффективные способы, хотя бы, вероятностные — генерим случайное большое число и тестируем каким-нибудь Миллером-Рабином.

  • Хватит спорить про функциональное программирование и ООП
    0

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

  • Хватит спорить про функциональное программирование и ООП
    +1

    Это да. Мне хотелось продемонстрировать не столько чистое ФП, сколько его выразительность и гибкость. Если требуют решето именно Эратосфена и именно двойным циклом и именно с вычёркиваем, то… и это можно.
    А так, есть классные функциональные решения тут и тут. Но они уже не совсем то решето, о котором обычно рассуждают.
    Для своих нехитрых нужд я бы взял ваш однострочник и не заморачивался. Для криптографии какой-нибудь — быстрый императивный код через FFI, но я в ней мало смыслю, если честно.

  • Хватит спорить про функциональное программирование и ООП
    +1

    Кормящим матерям и путникам не возбраняется :)

  • Хватит спорить про функциональное программирование и ООП
    +1

    Спасибо, это, действительно, многовато для комментария. Пройдёмся по вашим вопросам.


    • Вывод типов в ФП распространяется не только на объявление параметров/констант, но и на типы всех выражений и функций. И работает он в "обе стороны", то есть, я могу явно написать тип определяемой функции, а потом спросить у компилятора каким должен быть тип того или иного (любого) подвыражения в её теле, используя т.н. дырки, (holes). Это лучше показать, но не в комментарии. С другой стороны, я могу написать тело функции, не указывая вообще ни одного типа, и компилятор постарается вывести самый общий полиморфный тип для неё (в статике, на этапе компиляции). Если не выйдет, он скажет что именно ему не понятно, и это будет либо моя ошибка, либо неоднозначность задачи, требующая явного сужения типа, что тоже полезно и открывает глаза на программу. Особенно приятно общаться с компиляторами PureScript или Elm, но и ghc в последние годы становится более дружелюбным (или это я учусь понимать его?). А вот работа с литералами-константами (из примера с var), как раз часто требует уточнения, поскольку в Haskell выражение 5 может означать либо целое число, либо число с плавающей точкой, либо, вообще, какой-нибудь тип-обертку типа Sum или Max. Компилятор очень постарается понять из контекста, что же это такое, но программист может его здорово запутать :)
    • Я не преувеличил роль вывода типов в ФП в серьёзных исследованиях. Многие наши замечательные современники — асы ФП, создавшие целые концепции, вроде линз, кондуитов, или свободных монад, активно используют интерпретатор GHCi, как основной инструмент, получая от него ответы на вопросы (скажем, какой функтор нужно подставить в линзу, чтобы превратить его в геттер или сеттер) и целые доказательства (см. изоморфизм Карри-Ховарда) для своих идей, о чём с удовольствием рассказывают на конференциях и в блогах.
    • Я нарочно взял словосочетание «типо-ориентированное программирование» в кавычки, это не официальный термин. Типы в ФП (типизированном) это нечто большее чем "множество допустимых значений, внутреннее представление данных и возможные операции над ними". Это основа дизайна программ, предоставляющая связи между типами и законы (в математическом смысле, а не в инженерном) им присущие. Типы параметризуют друг друга и сами образуют более общую структуру Kinds, всё это выводит работу с ними на уровень настоящего исчисления. Иногда это чертовски сложно и нетривиально, иногда чертовски красиво, но вот что для меня привлекательно: тут остаётся мало места традициям, моде и вкусовщине, присущим нашему делу. Вместо них теоремы, анализ и строгие выводы, которые можно использовать, спустя десятилетия и они не устаревают, а со временем получают развитие, как, например получилось с линейными типами.
    • На последний вопрос отвечу аккуратно: отсутствие изменяемого состояния (концептуальное) заставляет писать код, в котором проблем с параллелизмом и конкурированием существенно меньше. Я не стану утверждать рекламным голосом, что в рамках ФП параллелизм станет лёгким и приятным как никогда, и теперь с этим справится даже ваша мама. Сложности всё равно остаются, но они, скажем так, иного уровня, поприятнее, что-ли.
  • Хватит спорить про функциональное программирование и ООП
    +2

    Немного прокомментирую это решение. Я использовал Data.Vector.Unboxed.Mutable только для эффективности. От этой структуры здесь лишь конструктор M.replicate и функции доступа M.read b M.write. На месте этой структуры могли быть немутабельные векторы, Array (или даже список со всеми вытекающими проблемами эффективности). Программа и её результат бы от этого не изменились, только сигнатура. И то, её я не писал вручную, мне рассказал о ней компилятор :)


    Именно этот выбор определил то, что результат будет "жить" в IO. Но ровно эту же программу можно погрузить в чистую ST, в любом случае, она принципиально императивна, что не помешало мне реализовать её в Haskell.


    Циклы реализованы через рекурсию, вполне прямолинейно, в виде хвостового вызова. Из магии только монадические штучки >> и >>=, которые можно рассматривать как пайпы.
    И, наконец, я нарочно написал тело цикла sieve так, чтобы он "выглядел" как изменение переменной m, чтобы показать, что если уж мы мыслим наш алгоритм императивно, то его можно и выразить императивным кодом, не изменяя принципам ФП. Мы не монахи и императивность не грех, если всё делать аккуратно, с благословения компилятора.


    В бенчмарке использовался вариант с преобразованием в список, чтобы честно. Оно добавило константу, но оставило асимптотику.

  • Хватит спорить про функциональное программирование и ООП
    +4

    Вот решение, вполне прямолинейное и наивное.


    import Control.Monad.Primitive
    import qualified Data.Vector.Unboxed as V
    import qualified Data.Vector.Unboxed.Mutable as M
    
    eratosphenesArray :: Int -> IO (M.MVector (PrimState IO) Bool)
    eratosphenesArray size = M.replicate size True >>= sieve 2
      where
        sieve k m
          | k*k >= size = return m
          | otherwise   = do
              next <- M.read m k
              m <- if next then fill (2*k) k m else pure m
              sieve (k+1) m
    
        fill n k m
          | n+k > size = return m
          | otherwise  = M.write m n False >> fill (n+k) k m

    заполняет массив значениями True и False согласно оригинальному замыслу, в двух вложенных циклах (sieve и fill). Сигнатура страшная, но алгоритм прост как пробка.
    Если надо список простых чисел (индексы со значениями True), то можно написать такое:


    eratosphenes :: Int -> IO [Int]
    eratosphenes size = do
      a <- eratosphenesArray size
      elemIndices True . V.toList <$> V.freeze a

    Результаты прогона на ноуте (сплошная линия — n*log(log(n))):
    image

  • Хватит спорить про функциональное программирование и ООП
    0

    Да, main не функция. Сказывается привычка :) Это точка входа — выражение, которое вычисляется при запуске программы.

  • Хватит спорить про функциональное программирование и ООП
    0

    Ага! спасибо, посмотрю, operational переводится лучше, но они не тождественны, а связаны через Coyoneda. Интересно, сам Олег Киселёв как их называет?

  • Хватит спорить про функциональное программирование и ООП
    0

    Придется, но это, правда, не "очень сложно"!

  • Хватит спорить про функциональное программирование и ООП
    +3

    Мне импонирует ваше стремление разобраться! Переменное состояние и сайд-эффекты в ФП исключены вовсе не ради инкапсуляции, вернее не только ради неё. Если мы готовы принять это ограничение, то становятся возможными:


    • Однозначный вывод типов по Хиндли-Милнеру, существенно более полезный, чем var или auto, поскольку он проникает на все уровни программы от сигнатуры функции до самых мелких её деталей. И это не для экономии нажатий клавиш при определении функции, а для настоящей и осознанной типобезопасности. Компилятор в чистых типизированных ФЯП не тестировщик и не надзиратель, а друг и помощник, подсказывающий и объясняющий программисту даже не в чём состоит ошибка, а что ему писать дальше. Это не автодополнение IDE доступных полей и методов, а подсказка сложных и очень нетривиальных решений, которые потом могут превратиться в научные разработки. Это круто и от этого уже трудно отказаться!
    • "Типо-ориентированное программирование", несколько более широкое, чем data-driven design, поскольку на первый план выступают алгебраические свойства типов (и функций над ними), не нарушаемые изменением состояния.
    • Внятная и доказуемая денотационная семантика, equational reasoning, настоящее unit-тестирование с автогенерацией тестов и property-based тестированием (Quickcheck появился в Haskell и только потом разошёлся по языкам).
    • Выбор стратегии вычислений (строгая/ленивая), в чистом ФП не меняющая результат, а влияющая лишь на эффективность программы и на способы её декомпозиции (в ленивом Haskell декомпозиция вкусна до безобразия, одни только гиломормизмы и fusion чего стоят!).
    • Принципиальное отсутствие в многопоточности гонок, и проблем с общими ресурсами.
    • Наконец, осознанная свобода в выборе и конструировании семантики позволяет легко (правда, легко, монады не сложнее какого-нибуть преобразования Фурье или Лапласа, хоть и "ломают" по началу мозг) эмулировать и переменное состояние и эффекты, но со всеми перечисленными выше плюшками!
      Отсутствие сайд-эффектов и переменных не цель ФП, а средство достижения гораздо больших целей. И, более того, если надо, я всегда могу их себе завести, но они гарантированно будут локальными и с тонко настраиваемой семантикой.
  • Хватит спорить про функциональное программирование и ООП
    0

    Это, действительно, было бы очень полезно и для практики и для участия в холиварах с пруфами :)
    Вопрос не по теме. Присмотрел для перевода материал по freer monad, но сижу, чешу в затылке, как корректно перевести на русский freer: "ёщё более свободные", "освобождённые"… есть ли устоявшийся термин?

  • Хватит спорить про функциональное программирование и ООП
    0

    О, знаменитая статья. Но время идёт и приведённое в ней решение уже выглядит переусложнённым. Но это очень, очень полезная статья!

  • Хватит спорить про функциональное программирование и ООП
    0

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


    Для RosettaCode я писал "Змейку" на Haskell. И там генерация случайного расположения "еды" для змеи вся прячется в одной полиморфной функции в рамках MonadRandom, так как будто бы генератор у нас есть и он возьмёт затравку, когда понадобится, но нам пока не важно откуда. При этом код выглядит так, будто бы в мире змейки уже существуют все будущие координаты в которых еда "случайно" появится (как в беременной самке тли хранятся уже беременные самки и т.д.), это следствие ленивости.
    Когда же, наконец, мы в точке входа в программу (функция main) инициализируем змейкин мир, тогда в качестве экземпляра MonadRandom оказывается IO (без неё-то всё равно никак) и вот ни пользователь, ни змейка уже "не знают" где появится очередной фрукт.
    Это может показаться трюкачеством, но по-существу вся программа говорит только о том, что делать змейке, получившей сигнал от пользователя или при встрече с едой, и не более. Откуда возьмутся еда и сигналы пользователя, змейке знать не обязательно, об этом заботится только функция main, которая отправляет её в "реальный мир", либо в его детерминистическую эмуляцию (монаду ST) для отладки.


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

  • Хватит спорить про функциональное программирование и ООП
    +1

    Боюсь, что разочарую вас. Я имел в виду принципиальную возможность двигаться в этом направлении, если оставаться в рамках ФП. Пока же выкладки приходится делать "на бумажке". Но! Это возможно сделать. Я игрался с формальной верификацией некоего подмножества Java (практически, процедурным подмножеством, не выходящем за пределы Оберона) через триады Хоара. Очень быстро система неравенств становится неподъёмной, по моим ощущениям, экспоненциально. Инварианты циклов приходится писать самому, а это искусство и шаманство. После этого, возвращаясь к типам и индукции в ФП, с доказуемыми свойствами алгебраических структур, понимаешь, какой это кайф: надеть, наконец лыжи после того, как брел по пояс в мокром снегу. Но и лыжи пока приходится переставлять самому. Впрочем, пока ждем завтипов в Хаскеле, можно упражняться, отрабатывая технику на том что есть. Понятно же, что для того, чтобы написать грамотную рабочую спецификацию для автоматического верификатора, потребуется нехилая квалификация программиста. Но пока, насколько я вижу, именно ФП ведёт нас к возможности по-настоящему взлететь.

  • Хватит спорить про функциональное программирование и ООП
    0

    Запросто! Это как в задаче интегрирования. Есть алгебраический подход, а есть геометрический, есть спектральный, а есть аналитический по теореме Коши, наконец, можно численно, но и там, можно Симпсоном, можно Гауссом. Когда как удобнее. Полезно владеть всем.

  • Хватит спорить про функциональное программирование и ООП
    0

    Это не переменные, поскольку они не могут измениться, оставаясь в рамках процесса, описываемого функцией. Изменения происходят только на входе-выходе. Одно из отличий, в частности, состоит в том, что, в функциональном коде нет понятия времени и изменение наших "переменных" происходит одновременно: a,b = b,a+b, эквивалентное последовательности присвоений с буфером с = b; b = a+b; a = c.
    Это не лучше и не хуже нормальных переменных, просто если проще описать вычислительный процесс итеративным, ФП этому не сильно помешает.

  • Хватит спорить про функциональное программирование и ООП
    0

    А вот тут в дело вступает то обстоятельство, что задача трансляции (компиляции) по природе своей функциональна. Объектный код полностью определяется исходным кодом и текущим окружением (входными параметрами). На этом стоит вся система сборки дистрибутивов типа configure >> make. Так что если абстракция функциональности и теряется на каком-то уровне, то это происходит всегда одинаково и решение можно, в конце концов, верифицировать, добившись герметичности абстракции на верхнем уровне. GHC это уже умеет. То есть, нас, действительно могут не сильно интересовать детали реализации, покуда компилятор не нарушает некоторых нужных нам законов и инвариантов.

  • Хватит спорить про функциональное программирование и ООП
    0
    Т.е. числа Фибоначчи считаем по самой долгой рекурсии с 2 рекурсивными вызовами? :)

    Боже упаси! Рекурсивная функция не обязательно реализует рекурсивный процесс. если хочется организовать итерацию с "переменными" a и b:


    fib n = go 0 1 n
      where go a b n    
              | n == 1    = a
              | n == 2    = b
              | otherwise = go b (a+b) (n-1)

    что эквивалентно псевдокоду:


    a = 0; b = 1 
    while (true)
      if (n == 1) return a
      if (n == 2) return b
      a,b = b,a+b; n--

    Но в функциональном решении пропустить инициализацию "переменных" нельзя никак.

  • Хватит спорить про функциональное программирование и ООП
    0

    Самое главное — эти проверки и доказательства возможны, причём уже сейчас, так что глупо этим не пользоваться и спорить. Что подтверждает тезис, обозначенный в названии статьи.

  • Хватит спорить про функциональное программирование и ООП
    +3

    Функциональному подходу нет необходимости заполнять всё вокруг. Оно тяготеет к маленьким самодостаточным ортогональным блокам и если в императивной архитектуре такие блоки использовать, то они не помешают. В Фотране (куда уж императивнее!) с 80-х годов есть модификатор pure, указывающий и программисту и компилятору, что определяемая функция чистая, что позволяет им делать определённые выводы как о коде функции, так и о коде, ее использующем.
    Операционную систему не сделать чистой по определению, но с некоторыми задачами ФП справляется отлично. Например, на моём ноутбуке, а также на домашнем и на рабочем десктопах стоит NixOS (Linux-дистрибутив построенный вокруг функционального ленивого (а значит, чистого) пакетного менеджера Nix), c оконным менеджером XMonad. И мне нужна их декларативность при настройке и переносе решений с машины на машину. Но это не позволяет мне говорить, что ФП решает все проблемы. Но оно очень неплохо встраивается в императивные решения, поскольку предлагает… чистые функции :)

  • Хватит спорить про функциональное программирование и ООП
    +9

    Ну, и во многих областях это оправдано. Так появилась поддержка длинных целых и рациональных чисел с одной стороны, и всё более мощные символьные движки в повседневных задачах. Так что не зря упирались теоретики!

  • Хватит спорить про функциональное программирование и ООП
    +10

    Про вилку и ложку — это хорошо! А ведь есть ещё палочки и ножи всякие… Споры о том "что круче" парадоксальны в своей бесполезности. Ответ очевиден — лучше научиться владеть всеми приборами, и это не мешает иметь среди всех приборов любимый.
    Например, я люблю считать на логарифмической линейке, хотя каждый день ею, конечно, не пользуюсь, для этого есть калькулятор. Но опыт работы с линейкой даёт зримое представление о преобразованиях, соответствующих вычислениям. Движения ползунка и бегунка превращают вычисления в буквальную манипуляцию: в преобразования руками, от которых приходит интуитивное геометрическое представление о поле чисел, имеющее под собой глубокое теоретико-групповое основание. Ответ не берётся из ниоткуда, а возникает в контексте масштаба и своеобразного "ландшафта" вычислений, так что проверка его правильности начинает согласовываться с интуицией. Это как знание города помогает понять, как тебя везёт таксист.
    Подобные опыт и интуицию даёт работа в Лиспе и Хаскеле. С ними программа на JS или C# превращается не просто в набор рецептов, инструкций, а в те или иные вычислительные схемы или математические структуры с присущими им свойствами.
    Но, полагаю, даже если жаркие дискуссии на темы "нужна ли программистам математика", "что круче: ФП или ООП" и т.п. постепенно отомрут, на смену им придёт что-то вроде: "кто круче, как программист: ИИ, корректно выполняющий ТЗ, или человек с его творчеством?". Мы уже видим начало этой эпохи, споря можно ли доверять машине управлять автомобилем на дороге.

  • Аппликативные регулярные выражения, как свободный альтернативный функтор
    +1

    Думаю, для простоты примера. Регулярки, действительно знакомы всем, а контекстно-свободные грамматики, тем кто уже хоть немного знаком с теорией языков. К тому же, кроме злополучного many, в определениях примеров выражений не используется рекурсия материнского языка (Haskell), а именно она позволяет расширить регулярную грамматику до КС. Возможно, автор хотел показать, что хоть мы и создали EDSL, наследующий полноту по Тьюрингу от вмещающего языка, он сам по себе уже обладает вычислительной мощностью НКА. Впрочем, это уже мои домыслы :) но меня привлекла именно минимальность исходных посылок: создали элементарный тип, ввели его в свободную структуру и рраз! имеем конструктор, потом определили аглебру и рраз! готов простой исполнитель.

  • Аппликативные регулярные выражения, как свободный альтернативный функтор
    +2

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


    fizzbuzz = max <$> "fizz" `when` (`divisibleBy` 3)
                    <> "buzz" `when` (`divisibleBy` 5)
                    <> "quux" `when` (`divisibleBy` 7)
                   <*> show
      where
        when x p y = if p y then x else mempty
        divisibleBy x n = x `mod` n == 0
  • Монады с точки зрения программистов (и немного теории категорий)
    0

    Примером декомпозиции в Haskell может быть реализация игры "Змейка" на RosettaCode: http://rosettacode.org/wiki/Snake#Haskell

  • Монады с точки зрения программистов (и немного теории категорий)
    0

    Думаю, вполне правильно. Для управления состояниями строгие и императивные языки в монадах не нуждаются. Но монады и в этих языках позволяют организовывать поток вычислений с весьма изощрённой логикой. Когда-то я определил для себя, что монады позволяют мне перегрузить операторы := и ; определив в них свою семантику. Это случилось во времена Лиспа — не чистого и строгого языка.

  • Монады с точки зрения программистов (и немного теории категорий)
    +1

    Хороший пример, спасибо!
    Идиоматичное решение здесь может быть таким: создадим чистую полиморфную функцию, которая соответствует вложенному циклу:


    mkComps :: PrintfType a => Int -> [a]
    mkComps n = [printf "%u <%s> %u\n" x (show (compare x y)) y
                | x <- [0..n]
                , y <- [0..n]] 

    где compare :: Ord a => a -> a -> Ordering — это чистая библиотечная функция, соответствующая вашей.


    Эту функцию можно вызвать в чистом контексте:


    > concat (mkComps 3) :: String
    "0 <EQ> 0\n0 <LT> 1\n0 <LT> 2\n0 <LT> 3\n1 <GT> 0\n1 <EQ> 1\n1 <LT> 2\n1 <LT> 3\n2 <GT> 0\n2 <GT> 1\n2 <EQ> 2\n2 <LT> 3\n3 <GT> 0\n3 <GT> 1\n3 <GT> 2\n3 <EQ> 3\n"

    или в IO


    main = sequence_ (mkComps 3)

    > main
    0 <EQ> 0
    0 <LT> 1
    0 <LT> 2
    0 <LT> 3
    1 <GT> 0
    1 <EQ> 1
    1 <LT> 2
    1 <LT> 3
    2 <GT> 0
    2 <GT> 1
    2 <EQ> 2
    2 <LT> 3
    3 <GT> 0
    3 <GT> 1
    3 <GT> 2
    3 <EQ> 3

    это был вывод в консоль.


    Здесь mkComps выполняет всю работу чистым образом, "производя" данные, а в main они превращается в вывод на печать. Благодаря параметрическому полиморфизму функция printf может возвращать как "чистую" строку, так и побочное действие. А благодаря ленивости языка никакого списка в памяти при этом не создаётся, хотя выглядит он подозрительно. На моём ноутбуке откомпилированный бинарник отправил в /dev/null 10000*10000 записей за 10 секунд.

  • Монады с точки зрения программистов (и немного теории категорий)
    0

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

  • Монады с точки зрения программистов (и немного теории категорий)
    0

    Представляете, а ведь мы так говорим не чтобы запутать, а наоборот, чтобы разобраться :) и ведь получается!

  • Монады с точки зрения программистов (и немного теории категорий)
    +1

    Абсолютно согласен. Мне хотелось показать, что ничего "магического", "неправильного" или чуждого идее ФП в функциях типа getLine нет. И вообще, разницу между императивным и функциональным подходом хотелось бы демистифицировать. Можно писать чистые функции на С, и даже выгоды от этого получать (в FORTRAN есть ключевое слово pure, помогающее компилятору трансляции чистых функций), но это акт доброй воли. Можно в Haskell запихать всё в IO, но компилятор из помощника превратится в послушного толмача. А можно балдеть от оптимизаций gcc и ghc, подавая им правильно приготовленные программы, расширяя горизонты. Кайф!