LINUX.ORG.RU

Не понимаю, что такое замыкание (closure)

 ,


5

6

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

Но если прочитать вики, то «Замыкание (англ. closure) в программировании — функция, в теле которой присутствуют ссылки на переменные, объявленные вне тела этой функции и не в качестве её параметров (а в окружающем коде).» Ну и что? Получается просто, что процедура использует глобальные переменные, ничего примечательного тут вроде и нет.

Так что такое замыкание? Помогите разобраться.

Ответ на: комментарий от svu

Ээээ вообще ни одно из слов «формальная», «предназначенная для записи компьютерных программ» к английскому не относится.

Ээээ, почему? По-моему - вполне.

anonymous
()
Ответ на: комментарий от anonymous

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

svu ★★★★★
()
Ответ на: комментарий от anonymous

Перестает. Подмножество языка с резко формализованными правилами - это уже не настоящий язык.

svu ★★★★★
()
Ответ на: комментарий от anonymous

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

svu ★★★★★
()
Ответ на: комментарий от anonymous

факториал

Так и запишем — любая программа эквивалентна факториалу.

\f g h -> f (\x -> unlessM (g x) (h x)) альфа-эквивалентно исходному варианту (с (user-data) или (&rest user-datum) вместо x), экстенсиально/эта-эквивалентно \f g h -> f $ unlessM . g <*> h. Problems?

твой абстрактный вариант - который вообще не выполняет никакой работы.

Ты о чём? Замени test на main, компилируй и запускай — всё прекрасно работает.

Протекающая абстракция.

Ты таки не знаешь языка. m Bool от Bool отличается только тем, что первый сам распаковывает Bool из m, а второй ждёт что это сделают за него, иначе говоря, первый ждёт «генератор» (в случае IO) из которого нужно взять значение, а второй — само значение. Сам этот первый вариант нужен чисто ради сокращения записи кое-где.

unless должно принимать обычный Bool, а не m Bool.

И вот unless уже есть в Control.Monad. unlessM — нет. Мне нужен второй только для более краткой записи (попробуй напиши syn с unless — \f g h -> f (\x -> g x >>= (`unless` h x)), хочется обвёртки unlessM, а (g x) не всегда может возвращать голый Bool уже в силу специфики IO).

Как минимум, исправь это.

Меня всё устраивает :) Вообще, мы занимаемся тут какой-то ерундой — я не вижу задачи, смысла в таком syn и вообще в приведенном коде — не стал бы так писать.

Вопросом может быть разве что — законно ли использовать (>>=), (<*>), (<$>) и т.п. для IO, списков, Maybe и функций, например (то есть всё в куче)? Если нет, то зачем тогда вообще в стандарте все эти классы типов и инстансы для всех этих типов данных (для функторов и монад они ещё и вместе с Prelude импортируются везде).

Напоминаю, что в результате, посмотрев на syn, я должен знать о семантике все то же самое, что я знаю о ее семантике, посмотрев на syncronize из оригинала.

Напомню, я ок с тем фактом, что кто-то, посмотрев на syn, не знает чего-то о семантике :)

Кстати, надо бы наверное сделать специальный обфускатор для хаскеля.

Вроде есть такое — pointfree или как-то так, у lambdabot работает, в том числе.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

\f g h -> f (\x -> unlessM (g x) (h x)) альфа-эквивалентно исходному варианту (с (user-data) или (&rest user-datum) вместо x), экстенсиально/эта-эквивалентно \f g h -> f $ unlessM . g <*> h. Problems?

g должно быть обычным предикатом A -> Bool. Никаких m.

Ты о чём? Замени test на main, компилируй и запускай — всё прекрасно работает.

Так ты руками дописал. Твоя библиотечная функция, которая должна что-то делать, работы не выполняет, ты переложил выполнение работы на пользователя библиотеки. и (раз в данном случае ты - это он и есть), как пользователь дописал недостающие части.

Ты таки не знаешь языка.

Таки знаю.

m Bool от Bool отличается только тем, что первый сам распаковывает Bool из m, а второй ждёт что это сделают за него, иначе говоря, первый ждёт «генератор» (в случае IO) из которого нужно взять значение, а второй — само значение. Сам этот первый вариант нужен чисто ради сокращения записи кое-где.

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

Мне нужен второй только для более краткой записи

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

Меня всё устраивает :)

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

Если нет, то зачем тогда вообще в стандарте все эти классы типов и инстансы для всех этих типов данных (для функторов и монад они ещё и вместе с Prelude импортируются везде).

А зачем в обычный языках есть for/if и т.п.? :) Да, хаскель - язык с самым сложным синтаксисом среди всех ЯП. Да, в нем есть десятки синтаксических конструкций (те самые >>=, <*> и прочее). Да, они зачастую нужны и без них получится говно, а не код :)

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

Напомню, я ок с тем фактом, что кто-то, посмотрев на syn, не знает чего-то о семантике :)

Еще раз - если семантика ф-и из ее кода невыводима, значит функция просто _не решает задачу_. И все. Это очень хороший критерий.

Вроде есть такое — pointfree или как-то так, у lambdabot работает, в том числе.

Так ты, видимо, им и воспользовался? Ну то есть ты же не идиот, чтобы руками выполнять чисто механическую работу?

anonymous
()
Ответ на: комментарий от anonymous

g должно быть обычным предикатом A -> Bool.

Ну давай (пишу специально без комбинаторов):

syn sender pred receiver = sender (\datum -> unless (pred datum) (receiver datum))

pred это a -> Bool. И теперь скажи, что мне передавать для pred тут

synToHT sender key ht = syn sender (\val -> isJust <$> HT.lookup ht (key val)) (\val -> HT.insert ht (key val) val)
--                                                  ^ упс, в смысле комбинатор затесался

вместо (\val -> isJust <$> HT.lookup ht (key val)), так как получается «Couldn't match expected type `Bool' with actual type `IO Bool'». Иначе говоря, как преобразовать IO Bool в Bool — наш мутабельный контейнер возвращает lookup в IO, так как он мутабельный — сейчас значение по ключу есть, а после мутации его может не быть, эффекты явно зашиты в IO.

Исправь.

Ну удали чистоту, explicit effects и монадки из хаскеля — исправлю. Вот в скале это может быть A -> Bool.

Твоя библиотечная функция, которая должна что-то делать, работы не выполняет

Тогда если её удалить, то всё должно продолжить работать, но от неё (synM) зависит synToHT, от которой зависит synBinaryUsersToHT и synPasswdToHT, от которых зависит test — неувязочка.

как пользователь дописал недостающие части

Да не, в synM передаётся всё как у свизарда в synchronize, просто так просто, что выродилось в банальности.

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

anonymous

Это так не работает :) Хватит вещать — он корректно работает (это проверяется тестом), он решает «задачу» полно, то есть разная хрень передаётся этому комбинатору syn, так что эта хрень, как бы, отвязана друг от друга. Ну и наличие synToHT, например, это хороший шаг, не говоря уже про «красивую» вербозность CL.

Так ты, видимо, им и воспользовался?

Нет, обфускация проводится до тех пор, пока сохраняется «субъективная очевидность». Но проблема в том, что когда код — однострочники (или около того), трудно что-то сказать про стиль.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Нет, обфускация проводится до тех пор, пока сохраняется «субъективная очевидность». Но проблема в том, что когда код — однострочники (или около того), трудно что-то сказать про стиль.

То есть запутать функцию так, чтобы без типа в принципе нельзя было узнать, что она делает - это «сохранил очевидность»? :)

И какой вообще смысл в обфускации? Ну то есть даже до «сохранения очевидности» на кой хрен ее проводить надо?

pred это a -> Bool. И теперь скажи, что мне передавать для pred тут

Протекшая абстракция, да. Это проблема выбранной абстракции, что она течет. Поменяй так, чтобы не текла, это же уже не мои проблемы, как ты поменяешь :)

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

Ну удали чистоту, explicit effects и монадки из хаскеля — исправлю.

Это следует понимать как «качественного решения на хаскеле получить нельзя» (то есть если все названное убрать - будет не хаскель, очевидно)?

Тогда если её удалить, то всё должно продолжить работать, но от неё (synM) зависит synToHT, от которой зависит synBinaryUsersToHT и synPasswdToHT, от которых зависит test — неувязочка.

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

Еще раз, твоя функция должна (ну или там набор ф-й):

Получить какую-то коллекцию, обойти ее и применить некоторую доп функцию к элементам коллекции, удовлетворяющим переданному предикату.

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

не говоря уже про «красивую» вербозность CL.

Так ты задачу уже сразу переделал так, чтобы на хаскеле оно хоть более-менее сносно выглядело, иначе у тебя там было бы говна на сотню строк :)

Если написать на CL то, что у тебя, то и будет тот же двустрочник. Ты его сам и приводил, кстати.

Хватит вещать — он корректно работает (это проверяется тестом), он решает «задачу» полно

Ты решил задачу для некий конкретных коллекций/предикатов, но общего решения не предоставил.

И при этом у тебя еще и абстракции жутко текут, то есть ИРЛ за такой косяк тебе руки вырвут.

anonymous
()
Ответ на: комментарий от anonymous

Закругляемся.

Ну то есть даже до «сохранения очевидности» на кой хрен ее проводить надо?

hint: доменное имя третьего уровня. Надо было написать disclaimer — 1) я не знаю, нафига такой synchronize нужен, 2) показалось забавным, что он пишется так, как я написал с Applicative.

Ничего менять я не буду, так как аналог уже написан (моё мнение) — я переписывал один в один и m Bool оказался необходим, если по-твоему это no-go, то да — при таком обзоре «качественного решения на хаскеле получить нельзя». Monad m => ... m a ..., для полиморфного или конкретного a не может быть «протекшей абстракцией» если есть правильная интерпретация, у unlessM она есть.

Потому что если вывести этого из кода нельзя - значит он не делает.

Как скажешь.

Ты решил задачу для некий конкретных коллекций/предикатов, но общего решения не предоставил.

И я так отвечаю потому, что тут ты демонстрируешь непонимание обоих вариантов — они, блин, идентичны насколько это возможно (да, в формате поста на ЛОРе я считаю различия между f $ g <*> h, f $ \x -> g x (h x) или с «нормальными именами» незначительными).

quasimoto ★★★★
()
Ответ на: комментарий от anonymous

Кстати, в этом треде anonymous уже философствовал в духе Тагора (в споре с Эйнштейном), так что нужно быть бдительным :)

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

2) показалось забавным, что он пишется так, как я написал с Applicative.

Но так же все что угодно пишется, чего тут забавного?

Ничего менять я не буду, так как аналог уже написан (моё мнение) — я переписывал один в один и m Bool оказался необходим

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

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

Был бы он нормлаьным языком, можно было бы сделать как-то вот так:

syn pred proc = filter pred . fmap proc . fold (IO ()) >>

Коротко, просто, понятно, ничего не течет, функционал выполнен, семантика ясна.

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

Да нормальные имена или f $ g <*> h вместо f $ \x -> g x (h x) - это же так, мелкие претензии. Основные:

1. недореализованный функционал

2. протекающая абстракция

А имена с f $ g <*> h - ну это обычная хаскиупоротость, тут обсуждать вобщем-то и нечего, поможет только лечение.

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

anonymous
()
Ответ на: комментарий от quasimoto

И вообще (на счет m Bool) в монаду же принято поднимать как можно позже, зачем сразу говнякать всю конструкцию этим вашим IO? (и бтв писал бы тогда в типе IO, раз там по смыслу должно быть IO, а не какое-то невнятное m, ты же человек, ты понимаешь, как тип семантически ограничить, это чекер только наиболее общий вывести может). На самом-то деле у нас коллекция и не обязательно мутабельная.

anonymous
()
Ответ на: комментарий от anonymous

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

Вот именно. А почему — потому что эффекты отделены от чистых функций, _|_ всё ещё тут, в каждом типе, так что это не тотальный язык, но эффектов уже нет вне IO. То есть у самих языков разная семантика, поэтому не нужно удивляться что a -> Bool превращается в a -> IO Bool где-то. В тотальном языке с выделенной явно «частичностью» ещё веселее будет.

недореализованный функционал

Твоё непонимание — указывай где нереализованный функционал. Я даже больше сделал — passwd -> таблица (как в оригинале) и сериализация binary -> таблица. Через передачу synM кода генераторов, предикатов и приёмников — один в один.

протекающая абстракция

nope.

оказывается, код никому читать не надо и понимать не надо (не важно, что из кода непонятно, что функция делает, главное, чтобы оно как-то работало, если в main подставить!)

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

quasimoto ★★★★
()
Ответ на: комментарий от anonymous

Есть такая вещь — каждое чистое поднимется в монадическое с помощью return, но не всегда обратно, так что если я сделаю c m a, то я буду работать и с чистым (когда оно поднимется) и с эффектами (если это IO), если только a — более специальный вариант, только чистое (мутабельные коллекции по боку). Кроме того, у всякой функции с аргументом a есть версия с аргументом m a и так для всех аргументов — автоматического лифтинга нет. Просто в base нет версии unless с Bool в m. А не IO потому что unlessM _существует_ в теории монад — такое же m как и любое другое m из Control.Monad (там тоже всё существует, то есть выводится из аксиом монады).

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 2)
Ответ на: комментарий от quasimoto

nope.

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

Не, я сказал только что непонимание это проблема непонимающего

При чем тут _чье-то_ понимание? Мы же речь ведем о принципиальной невыводимости семантики кода из самого этого кода. И ты говоришь: «ну ок, по коду никто никогда не сможет сказать, что он делает. Ничего страшного же!»

указывай где нереализованный функционал.

Ты мне укажи, где он реализован. То есть набор функций, из кода которых выводимо, что:

обходится некоторая коллекция и проводится некоторое действие с элементами коллекции, удовлетворяющими некоторому предикату

Вот именно. А почему — потому что эффекты отделены от чистых функций

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

поэтому не нужно удивляться что a -> Bool превращается в a -> IO Bool где-то.

Так с чего там вдруг a -> IO Bool, если я согласно семнатике функции передаю чистый предикат, работающий с чистыми значениями?

anonymous
()
Ответ на: комментарий от quasimoto

Есть такая вещь — каждое чистое поднимется в монадическое с помощью return, но не всегда обратно

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

А не IO потому что unlessM _существует_ в теории монад — такое же m как и любое другое m из Control.Monad (там тоже всё существует, то есть выводится из аксиом монады).

Ну и что что существует? Нам то нужно конкретно IO, нам не надо, чтобы было абстрактное m. Любую m, которая не IO, надо запретить в данном случае же.

Нахрена нужна система типов вообще, если ты ей не пользуешься?

anonymous
()
Ответ на: комментарий от anonymous

При чем тут _чье-то_ понимание?

Потому что _ты_ смотришь на два куска кода на двух языках и не понимаешь. Иначе бы не спрашивал.

Ты мне укажи, где он реализован.

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

Там ИО в принципе нет

Там есть gethash и setf/gethash глобальной хэш-таблицы — в хаскеле это сразу IO (я могу, конечно, ST сделать, но лучше не нужно).

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Там есть gethash и setf/gethash глобальной хэш-таблицы

Какие еще хеш-таблицы, если функция должна для любой коллекции работать?

Ну вот. Если интересно — открывай и смотри где читается файл, где производится итерация по ленивому потоку распаресенных структур (у меня) и где они пушаться в таблицу.

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

Потому что _ты_ смотришь на два куска кода на двух языках и не понимаешь.

Да при чем тут я-то? Еще раз, мы говорим об _объективной невыводимости семантики_. То есть ее просто нельзя вывести. Никак. Никому. Ни мне, ни тебе - ну никому, вообще. В этом контексте, еще раз - при чем тут чье-то конкретное непонимание? И почему тогда мое? Почему не твое? Мы же находится в абсолютно идентичной ситуации незнания семантики.

anonymous
()
Ответ на: комментарий от anonymous

Ну и что что существует?

Пусть будет? Писать обобщённые на интерфейсы алгоритмы под каждую реализацию интерфейсов это комбинаторный взрыв количества кода (почему STL такая, какая она есть, например?).

quasimoto ★★★★
()
Ответ на: комментарий от anonymous

Какие еще хеш-таблицы, если функция должна для любой коллекции работать?

В том числе для хеш-таблиц. У свизарда единственный пример — passwd -> хеш-таблица, я его и сделал.

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

f $ unlessM . g <*> h :) Не, ну как в оригинале же.

Выводится семантик куча сразу для многих моделей.

Почему не твое?

Я ок.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Пусть будет?

Зачем? Это же вредно.

Писать обобщённые на интерфейсы алгоритмы под каждую реализацию интерфейсов

Зачем под каждую? Про абстракцию ты не слышал?

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

Еще раз, это чекер когда тип выводит, может вывести только наиболее общий. Но почти всегда этот тип некорректен (тем и плох полный тайп инференс, кстати), его надо семантически ограничить. Это уже может сделать только человек - и _должен_ делать.

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

anonymous
()
Ответ на: комментарий от quasimoto

В том числе для хеш-таблиц.

В том числе. Но не только ведь. С чего я должен чего-то в ИО поднимать, если у меня там обычный список будет, например? Это же бред. А если поднимать надо - то поднимать надо в монаду саму syn, а не ее аргумент.

У свизарда единственный пример — passwd -> хеш-таблица, я его и сделал.

Это просто чудесно. То есть ты предоставляешь совершенно ненужные точки расширения (вроде неуказанного инстанса аппликатива или монады), но зато там, где оно надо, ты как раз все прибиваешь гвоздями на основании частного случая.

f $ unlessM . g <*> h

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

Выводится семантик куча сразу для многих моделей.

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

anonymous
()
Ответ на: комментарий от quasimoto

Я ок.

Ну вот раз ок, то и покажи, как в f $ unlessM . g <*> h отвергнуть все эти «многие» семнатики и оставить одну нужную.

anonymous
()
Ответ на: комментарий от anonymous

Важно - имеет ли это смысл?

Ты говоришь про unless/unlessM? unless в стандарте с сигнатурой Monad m => Bool -> m () -> m () и реализацией unless False x = x; unless True _ = return () (то есть достаточно PointedFunctor). Возможно, нам не нужно возвращать [()], Just () или const () для unless True _ и x для unless False x, то есть не видно таких применений, но для IO, ST, обёрток над ними, трансформеров над IO (пример — ServerPart из Happstack), Free она имеет вполне полезный смысл. Поэтому я не хочу фиксировать m к IO — есть ST, IO или ST можно переименовать newtype-ом, есть трансформеры, могут иметь смысл другие монады (интерпретируемый eDLS на Free, например).

С чего я должен чего-то в ИО поднимать, если у меня там обычный список будет, например?

Используй чистую synA тогда, типа

> synA (`map` [1, 2, 3]) (\_ r -> if odd r then r else 0) (+1)
[0,3,0]

synM это вариант в котором предикат лезет внутрь монады, так специально сделано, вот:

Рассмотрим, например, такую задачу: синхронизировать учетные записи пользователей из источника с хранилищем. Разумеется, источник может быть любым (/etc/passwd, xml-file, бд...) ну и хранилище тоже произвольное (in-memory hash, sql, ldap, черте что).

В таком случае synchronize принимает процедуры которые со всем этим работают (/etc/passwd, xml-file, бд..., in-memory hash, sql, ldap, черте что), причём предикат лезет в состояние этого дела, я не знаю как ты представляешь её чистой.

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

Ты опять что-то говоришь не посмотрев код.

f $ unlessM . g <*> h

Тут у нас (.) слева от (<*>), так что однозначно функции для Applicative, потом unlessM, то есть вариант unless, то есть монады внутри, почему не IO я сказал. Дальше объяснять не больше чем в (defun syn (f g h) (funcall f (lambda (x) (unless (funcall g x) (funcall h x))))).

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Ты говоришь про unless/unlessM?

Я говорю про любые функции с обобщенным типом.

Поэтому я не хочу фиксировать m к IO

не понимаю, при чем тут твое желание, если такова семантика ф-и?

Используй чистую synA тогда, типа

А это чего у предиката (\_ r -> if odd r then r else 0) два аргумента чтоли? Откуда второй вылез? И что-то он у тебя нифига не Bool возвращает.

Ты опять что-то говоришь не посмотрев код.

Ты же сам все подтверждаешь.

Тут у нас (.) слева от (<*>), так что однозначно функции для Applicative, потом unlessM, то есть вариант unless, то есть монады внутри, почему не IO я сказал.

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

anonymous
()
Ответ на: комментарий от quasimoto

Ну и да:

могут иметь смысл другие монады (интерпретируемый eDLS на Free, например).

Не могут, в том и дело. Никакие кроме IO.

anonymous
()
Ответ на: комментарий от anonymous

Я говорю про любые функции с обобщенным типом.

Ну так претензии не ко мне — у when, guard(*), unless, forever или void из стандарта Monad (* MonadPlus), что может показаться странным (хотя не должно), а у меня у unlessM с synM.

не понимаю, при чем тут твое желание, если такова семантика ф-и?

Никакие кроме IO.

Повторяю:

есть ST, IO или ST можно переименовать newtype-ом, есть трансформеры

пример — ServerPart из Happstack

someRoute :: ServerPart Response
someRoute = dir "someRoute" $ do
  method POST
  param <- ... look ...
  boolParam <- ...
  unless boolParam $ do
    ... server part monad again ...
  ...

Это касается synM — я не могу сделать IO, иначе оно не будет работать когда должно (то есть в куче разных монад и трансформеров поведение которых «напоминает» IO).

Для unless ещё

могут иметь смысл другие монады

iteratees, pipes, free. Привожу пример с free:

import Control.Monad
import Control.Monad.Free

data DSL val cont
  = Read (val -> cont)
  | Write val cont
  | Value val

instance Functor (DSL val) where
  fmap f (Read g) = Read (f . g)
  fmap f (Write val cont) = Write val (f cont)
  fmap _ (Value val) = Value val

w :: val -> Free (DSL val) ()
w x = liftF $ Write x ()

v :: val -> Free (DSL val) cont
v = liftF . Value

r :: Free (DSL val) val
r = liftF $ Read id

t :: Free (DSL String) ()
t = do
  str <- r
  unless (str /= "print") $ do
    w "print"
    w "print"
    w "print"

eval :: Free (DSL String) cont -> IO String
eval (Free (Read f)) = getLine >>= eval . f
eval (Free (Write x c)) = putStrLn x >> eval c
eval (Free (Value x)) = return x
eval (Pure _) = return "end"

А это чего у предиката (\_ r -> if odd r then r else 0) два аргумента чтоли?

Это не предикат, это ленивая-функция-фильтр g в f $ \x -> g x (h x) (это f $ g <*> h в случае функций). Предикат это белее специальное поведение — аналогично тому как было с unlessM реализуем его с помощью элиминатора типа Bool:

choose :: Bool -> a -> a -> a
choose False x _ = x
choose True _ y = y

synB :: ((a -> a) -> b) -> (a -> Bool) -> (a -> a) -> b
-- synB f p h = synA f (\x y -> choose (p x) x y) h
synB f p = synA f $ choose =<< p
-- synB f = synA f . (choose =<<)
-- synB = synA >>> (<<< (>>= choose)) -- <-- понятно, не правда ли? :)

-- > synB (`map` [1, 2, 3]) odd (+1)
-- [2,2,4]

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

Это же осталось показать для первого куска кода тут.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Ну так претензии не ко мне

А к кому? Ты же код пишешь.

Повторяю:

Ну тогда тебе надо сделать свой собственный тайпкласс MyMonad, который будет обертывать обычные монады и с которым и будет работать synM.

iteratees, pipes, free. Привожу пример с free:

И тут аналогично.

Хотя еще раз - ни с какими другими монадами оно работать не должно. Нету ни одной монады кроме IO, в котором оно было бы осмысленно.

Это не предикат

А должен быть предикат. Исправь.

Это же осталось показать для первого куска кода тут.

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

anonymous
()
Ответ на: комментарий от anonymous

хаскель - язык с самым сложным синтаксисом среди всех ЯП. Да, в нем есть десятки синтаксических конструкций (те самые >>=, <*> и прочее).

Не отличаем стандартную библиотеку от синтаксических конструкций?

ovk48 ★★★
()
Ответ на: комментарий от ovk48

Так haskell без всех этих плюшек бесполезен и даже неинтересен.

anonymous
()
Ответ на: комментарий от ovk48

Не отличаем стандартную библиотеку от синтаксических конструкций?

А с чего ты взял, что синтаксическая конструкция не может быть библиотечной функцией? Семантически это все - синтаксические конструкций и используются они в качестве синтаксических конструкций, как любой for или while. А зачастую их семантика еще и сложнее.

anonymous
()
Ответ на: комментарий от anonymous

А к кому?

К общепринятым соглашениям, в том числе стандартным. Ещё раз:

unless :: Monad m => Bool -> m () -> m ()

forever :: Monad m => m a -> m b

При этом, например

> unless False []
[]
> unless True []
[()]
> unless True [(),()]
[()]
> unless False [(),()]
[(),()]

> forever []
[]
> forever [1]
  C-c C-cInterrupted.

-- и т.д.

казалось бы — нафига? Но эти инстансы Monad [], Monad Maybe, Monad ((->) r) и т.п. есть, весь код из Control.Monad для них тоже есть, законен и жить не мешает.

Если так посмотреть — весь код из <algorithm> и т.п. в C++ это одна сплошная «протекающая абстракция», потому что работает для чего угодно что претворяется итераторами, функторами и т.п., даже если это что-то совсем бессмысленное (и без концептов ситуация ещё хуже).

Также, должны быть

forever :: Monad m => m () -> m Void

но Void нет в стандарте.

Ещё, о чём речь была:

filter :: (a -> Bool) -> [a] -> [a]
--        ^ предикат

filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
--                          ^ сюда смотри

два варианта, аналогично synB и synM:

synB :: ((a -> a) -> b) -> (a -> Bool) -> (a -> a) -> b
--                         ^ предикат

synM :: Monad m => ((a -> m ()) -> t) -> (a -> m Bool) -> (a -> m ()) -> t
--                                             ^ и тут

Ну тогда тебе надо сделать свой собственный тайпкласс MyMonad

MyMonad не гарантирует, что инстансы для всего что Monad не появятся чудесным образом (как orphans).

Есть MonadIO. Пример — MonadIO, unless и замыкание на константные и мутабельные значения:

progressBar :: (MonadIO m, MonadIO m') => m () -> m () -> Int -> Int -> m' (Int -> m ())
progressBar drawCell drawEnd normedN n = do
  iRef <- liftIO $ newIORef 0
  return $ \di -> do
    i <- liftIO $ readIORef iRef
    let norm x = x * normedN `div` n
        i' = i + di
    unless (i < 0) $
      if i' >= n
      then do
        liftIO $ writeIORef iRef (-1)
        replicateM_ (normedN - norm i) drawCell
        drawEnd
      else do
        liftIO $ modifyIORef iRef (+ di)
        replicateM_ (norm i' - norm i) drawCell

test :: Int -> Int -> Int -> IO ()
test normedN n d = do
  draw <- progressBar (putChar '+') (putChar '\n') normedN n
  replicateM_ (2 * n) $ threadDelay 100000 >> draw d

Но MonadIO не покрывает ST, так что тут не годится.

А писать на каждый чих новый класс и париться с поддержкой 100500 инстансов неохота. Опять же, кому мешают «бессмысленные» (в строгом смысле у них есть «смысл», то есть интерпретация — «работает известным для каждой монады образом») варианты synM?

Нету ни одной монады кроме IO, в котором оно было бы осмысленно.

Давай считать. IO. Счётное количество обёрток над IO:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype MyIO a
  = MyIO { runMyIO :: IO a }
  deriving ( Functor, Monad, MonadIO )

-- > runMyIO $ liftIO $ print 1
-- 1

-- synToMyIO_HT :: (Eq k, Functor m, Hashable k, HashTable h, MonadIO m) =>
--                 ((v -> m ()) -> t) -> (v -> k) -> IOHashTable h k v -> t
synToMyIO_HT :: (Eq k, Hashable k, HashTable h) =>
                ((v -> MyIO ()) -> t) -> (v -> k) -> IOHashTable h k v -> t
synToMyIO_HT sender key ht = synM
  sender
  (\val -> isJust <$> liftIO (HT.lookup ht (key val)))
  (\val -> liftIO $ HT.insert ht (key val) val)

ST (http://hackage.haskell.org/package/hashtables):

import qualified Data.HashTable.ST.Basic as ST_HT

synToST_HT :: (Eq k, Hashable k) =>
              ((v -> ST s ()) -> t) -> (v -> k) -> ST_HT.HashTable s k v -> t
synToST_HT sender key ht = synM
  sender
  (\val -> isJust <$> ST_HT.lookup ht (key val))
  (\val -> ST_HT.insert ht (key val) val)

Счётное количество обёрток над ST:

newtype MyST s a
  = MyST { runMyST :: ST s a }
  deriving ( Functor, Monad )

synToMyST_HT :: (Eq k, Hashable k) =>
                ((v -> MyST s ()) -> t) -> (v -> k) -> ST_HT.HashTable s k v -> t
synToMyST_HT sender key ht = synM
  sender
  (\val -> isJust <$> MyST (ST_HT.lookup ht (key val)))
  (\val -> MyST $ ST_HT.insert ht (key val) val)

Трансформеры — http://hackage.haskell.org/package/mtl, например http://hackage.haskell.org/packages/archive/mtl/latest/doc/html/Control-Monad...

data MyEnv = ...

instance IsString MyEnv where
  ...

type MyIOWithState a = StateT MyEnv IO a

-- synToMyIOWithState_HT
--   :: (Eq k, Functor m, IsString s, Hashable k, HashTable h, MonadIO m, MonadState s m) =>
--      ((v -> m ()) -> t) -> (v -> k) -> IOHashTable h k v -> t
synToMyIOWithState_HT :: (Eq k, Hashable k, HashTable h) =>
                         ((v -> MyIOWithState ()) -> t) -> (v -> k) -> IOHashTable h k v -> t
synToMyIOWithState_HT sender key ht = synM
  sender
  (\val -> isJust <$> liftIO (HT.lookup ht (key val)))
  (\val -> do
      put "insert..."
      liftIO $ HT.insert ht (key val) val)

тут надо обратить внимание на строчку с put которая аналогична строчке method POST из примера про ServerPart (который тоже некий StateT над IO) — она должна иметь эффект на _состояние_ которое разделяют все функции которые связанны вызовами в данном слое «монадического стека», если IO хватит всем, то нужно задаться вопросом — как реализовать такую строчу в IO (никак — либо таскать хэндлер, либо хаки с unsafePerformIO, либо опора на внешнее состояние в FFI, FS, DB, ...).

Прочие трансформеры из mtl и т.п. — тоже.

Обёртки над ними — тоже.

Ну и много ещё примеров может быть — как любые конструкции (классические трансформеры, iteratees, pipes) над IO, так и косвенно свободные от IO вещи вроде free (их можно интерпретировать в IO, так что смысл есть).

Исправь.

Читай моё последнее сообщение на которое отвечал — там найдёшь synB.

То есть задачу ты не решил - ведь если из кода нельзя вывести, что он делает Х, то значит, что код не делает Х.

Ты таки косишь под Тагора :) «Без ума стула нет!1», пофиг, что работает.

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

З.Ы. ещё раз попробую: должна быть «очевидна» эквивалентность реализаций

synB :: ((a -> Maybe b) -> t) -> (a -> Bool) -> (a -> b) -> t
synB s p = synA s (\v v' -> if not $ p v then Just v' else Nothing)
synB s p r = s $ \v -> if not $ p v then Just $ r v else Nothing
-- 
-- > synB (`map` [1, 2, 3]) odd (+1)
-- [Nothing,Just 3,Nothing]
-- 
-- аналогично
-- 
-- > (synchronize (lambda (f) (mapcar f '(1 2 3))) #'oddp #'1+)
-- (nil 3 nil)

synM :: Monad m => ((a -> m ()) -> t) -> (a -> m Bool) -> (a -> m ()) -> t
synM s p = synA s $ unlessM . p
synM s p = synA s $ \v v' -> unlessM (p v) v'
synM s p = synA s $ \v v' -> p v >>= (`unless` v')
synM s p = synA s $ \v v' -> p v >>= \b -> unless b v'
synM s p r = s $ \v -> p v >>= \b -> unless b $ r v

и то что наиболее общие сигнатуры у них именно такие, то есть что Applicative работает именно для ((->) r), иначе это просто незнание того что такое (<*>) и Applicative (вообще или в случае ((->) r)).

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

К общепринятым соглашениям, в том числе стандартным.

Ну то есть это общая проблема хаскеля. Но зачем тянуть ее в свое решение?

Если так посмотреть — весь код из <algorithm> и т.п. в C++ это одна сплошная «протекающая абстракция»

Нет, там совершенно другая ситуация.

А писать на каждый чих новый класс и париться с поддержкой 100500 инстансов неохота.

Ну просто только факториалы писать, да. А когда реальная задача - то надо усилия приложить. Оно всегда так.

Давай считать. IO. Счётное количество обёрток над IO:

Ну тогда ты обязан написать собственный тайпкласс, такой, что все его реализации - это обертки над IO и только они, и пусть он будет в типе.

Счётное количество обёрток над ST:

Для ST бессмысленно, только IO.

Читай моё последнее сообщение на которое отвечал — там найдёшь synB.

Ну чего я должен лазить по куче твоих постов и куски из них дергать? Дай уже вариант полного готового кода.

Ты таки косишь под Тагора :) «Без ума стула нет!1», пофиг, что работает.

Так у тебя же не работает. ТЗ не выполнено.

З.Ы. ещё раз попробую: должна быть «очевидна» эквивалентность реализаций

Но то, что они эквивалентны - не значит, что любая из них решает задачу. И уж тем более не значит, что она ее решает хорошо :)

то есть что Applicative

Я что-то в приведенных вариантах вообще аппликатива не вижу. Ну и в любом случае, бритва Оккама тут не дает применить аппликатив.

anonymous
()
Ответ на: комментарий от anonymous

У тебя что-то с тырнетом, наверно — ITT всё было, такое же бессмысленное и беспощадное как и исходное лямбдование на CL.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

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

anonymous
()
Ответ на: комментарий от anonymous

Работающий код это, очевидно, два поста с test

Не понимаю, что такое замыкание (closure) (комментарий)

Не понимаю, что такое замыкание (closure) (комментарий)

Потом началась анонимная экспертиза — «IO не нужно», «а, нет, таки нужно», «m Bool это протекающая абстракция», «в filterM тоже есть? а, ну проблемы хаскеля», и прочие вопросы типа «а как нам чистый вариант написать?» и образчики типа «ST бессмысленно» при том что «а что если я хочу просто список передать?» — ты определись уже и посмотри интерфейс hashtables, есть же чистые (ты сам хотел) хэш-таблицы в ST. Тем более не нужно учить обязан ли я писать собственный тайпклас или нет, мне лучше знать в данном случае — я вместе с «общей проблемой хаскеля» и собираюсь тянуть её в свои решения.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Работающий код это, очевидно, два поста с test

Это не работающий код, т.к. он противоречит ТЗ, мы уже это выяснили.

То есть что-то, конечно, этот код делает, но не то, что нужно. По-этому нету смысла его сравнивать с кодом, который делает то, что нужно.

anonymous
()
Ответ на: комментарий от quasimoto

Тем более не нужно учить обязан ли я писать собственный тайпклас или нет

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

anonymous
()
Ответ на: комментарий от quasimoto

Вот кстати мое решение на scheme:

(define solve values)

Это в стиле твоего решения на хаскеле. Абстрактное, универсальное, гибкое, покрывает максимум юзкейсов.

anonymous
()
Ответ на: комментарий от anonymous

мы уже это выяснили

Ты собеседника спросить забыл. Я не знаю о чём ты вообще говоришь и что тебе не нравится.

quasimoto ★★★★
()
Ответ на: комментарий от anonymous

Ну я понимаю, что ты этого писать не хочешь

Дело не в этом. Ты можешь ещё заявить, например, что солнце кубическое, это же не значит, что я должен тебя слушать. Просто так бывает — кто-то (тут — я) считает что ты не прав и, соответственно, никакие советы серьёзно не воспринимает.

(define solve values)

Клиника. У меня с точность до добавления import и s/test/main/ можно идти делать ghc -X... FileName.hs и ./FileName. И любые конверсии которые можно добавить на CL используя synchronize можно добавить у меня используя syn*.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Да я уже не раз перечислил, что не нравится и обосновал, почему не нравится.

anonymous
()
Ответ на: комментарий от quasimoto

Дело не в этом.

Да нет, именно в этом. Потому что иначе ты бы просто написал, чем и закрыл разговор.

Клиника.

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

У меня с точность до добавления import и s/test/main/ можно идти делать ghc -X... FileName.hs и ./FileName.

Ну это же лоэь прямая. А монадку написать? А логику обхода закодировать? Это боженька все с неба тебе спустит или откуда оно должно взяться?

anonymous
()
Ответ на: комментарий от quasimoto

Ну или бтв копия твоего решения будет

(define (syn f g h)
  (f (λ (x) (g x (h x)))))
Ты сам выкладывал, кстати.

Но почему-то оно отличается от оригинала. Как думаешь, почему? правильно - потому что это решение другой задачи. Решение исходной задачи на хаскеле выглядело предельно криво, потому ты задачу и поменял. И начал сравнивать решения разных задач, экий затейник.

anonymous
()
Ответ на: комментарий от anonymous

Ну это же лоэь прямая

Где-то оно у меня валялось. А вот же оно:

➜  ~  cat Syn.hs       
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}

import Data.Word
import Data.Maybe
import Data.ByteString.Lazy ( ByteString )
import qualified Data.ByteString.Lazy as BS
import qualified Data.ByteString.Lazy.Char8 as BS8
import Data.Binary
import Data.Hashable ( Hashable(..) )
import Data.HashTable.IO ( BasicHashTable, IOHashTable )
import Data.HashTable.Class ( HashTable )
import qualified Data.HashTable.IO as HT
import GHC.Generics
import Control.Applicative
import Control.Monad

synA :: Applicative f => (f b -> t) -> f (a -> b) -> f a -> t
synA f g h = f $ g <*> h

synB :: ((a -> Maybe b) -> t) -> (a -> Bool) -> (a -> b) -> t
synB f p = synA f $ \v v' -> if not $ p v then Just v' else Nothing

unlessM :: Monad m => m Bool -> m () -> m ()
unlessM p a = p >>= (`unless` a)

synM :: Monad m => ((a -> m ()) -> t) -> (a -> m Bool) -> (a -> m ()) -> t
synM s p = synA s $ unlessM . p

fromBinaryList :: Binary e => FilePath -> (e -> IO a) -> IO ()
fromBinaryList path f = mapM_ f =<< decodeFile path

synToHT :: (Eq k, Hashable k, HashTable h) =>
           ((v -> IO ()) -> t) -> (v -> k) -> IOHashTable h k v -> t
synToHT f key ht = synM f (fmap isJust . HT.lookup ht . key) $ HT.insert ht =<< key

data User = User
  { _login :: !ByteString, _uid :: !Word, _gid :: !Word
  , _name :: !ByteString, _home :: !ByteString, _shell :: !ByteString
  } deriving ( Show, Eq, Generic )
instance Binary User where

type MyHT = BasicHashTable ByteString User

synBinaryUsersToHT :: FilePath -> MyHT -> IO ()
synBinaryUsersToHT = (`synToHT` _name) . fromBinaryList

fromPasswd :: (User -> IO a) -> IO ()
fromPasswd f = mapM_ (f . readUser) . BS8.lines =<< BS.readFile "/etc/passwd" where
  readUser bs = case BS8.split ':' bs of
    [login, _, uid, gid, name, home, shell] ->
      User login (read $ BS8.unpack uid) (read $ BS8.unpack gid) name home shell

synPasswdToHT :: MyHT -> IO ()
synPasswdToHT = synToHT fromPasswd _name

main :: IO ()
main = do
  
  let root = User "root" 0 0 "root" "/root" "sh"
      user = User "user" 1 1 "user" "/user" "sh"

  encodeFile "/tmp/test" [root, user]
  ht <- HT.new
  synBinaryUsersToHT "/tmp/test" ht
  root' <- HT.lookup ht "root"
  user' <- HT.lookup ht "user"
  print (root, root', user, user')

  ht <- HT.new
  synPasswdToHT ht
  mapM_ print =<< HT.toList ht
➜  ~  ghc Syn.hs       
[1 of 1] Compiling Main             ( Syn.hs, Syn.o )
Linking Syn ...
➜  ~  ./Syn | head -n 3
(User {_login = "root", _uid = 0, _gid = 0, _name = "root", _home = "/root", _shell = "sh"},Just (User {_login = "root", _uid = 0, _gid = 0, _name = "root", _home = "/root", _shell = "sh"}),User {_login = "user", _uid = 1, _gid = 1, _name = "user", _home = "/user", _shell = "sh"},Just (User {_login = "user", _uid = 1, _gid = 1, _name = "user", _home = "/user", _shell = "sh"}))
("added by portage for mailbase",User {_login = "mail", _uid = 8, _gid = 12, _name = "added by portage for mailbase", _home = "/var/spool/mail", _shell = "/sbin/nologin"})
("added by portage for man",User {_login = "man", _uid = 13, _gid = 15, _name = "added by portage for man", _home = "/usr/share/man", _shell = "/sbin/nologin"})

идентично тому что было много дней назад и всё ещё работает. Не знаю, какую ещё монадку надо писать.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Не знаю, какую ещё монадку надо писать.

Да вот же:

synM :: Monad m => ((a -> m ()) -> t) -> (a -> m Bool) -> (a -> m ()) -> t

Monad m

Ну и аналог твоего решения выше привел, да.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.