Как называется мета-глагол (high-order function) "not-null"?
Хочется что-то короткое (3-4 буквы в одно английское слово.map
Maybe?
mapКоротко, в одно слово, подходит по смыслу. К сожалению, map "занято" под более общую операцию: применение f к списку. http://en.wikipedia.org/wiki/Map_%28higher-order_function%2...
Коротко, в одно слово, подходит по смыслу. К сожалению, map "занято" под более общую операцию: применение f к спискуmap - это операция, которая применяется к любому функтору. В общем виде. И нужно предоставить свидетельство, что Option является функтором.
Maybe?Хорошо подходит по смыслу, в похожем контексте используется в разнообразных проектах, короткое, в одно слово (очень близко к этому контрастное (отличается от других часто используемых глаголов).
Отторжение пока чувствую к нему. То ли потому что "придумано не нами (c)", то ли потому что это не совсем глагол. Не получается пока конкретно идентифицировать причины отторжения.
Сейчас буду использовать это слово. Открыт к другим предложениям. Какие еще варианты могут быть?
map - это операция, которая применяется к любому функтору. В общем виде. И нужно предоставить свидетельство, что Option является функтором.В чем суть предложение? Перегрузить поведение map для Option-а?
Возьмем задачу чуть шире. При обработке списков Option-ов возможны три поведения:
(null, 2, 3, null, 5, null, 7).F1(+1) ==> (null, 3, 4, null, 6, null, 8)
(null, 2, 3, null, 5, null, 7).F2(+1) ==> (1, 3, 4, 1, 6, 1, 8)
(null, 2, 3, null, 5, null, 7).F3(+1) ==> (3, 4, 6, 8)
При вводе нового глагола Maybe за F1 закрепляется название Map, а за F3 - Maybe. Вместо использования многосложных конструкций: F1 = Map, F3(f) = Filter(x => x != null).Map(f)
map внутри map (для option внутри list) - это flatmap.
map внутри map (для option внутри list) - это flatmap.Корни решения растут из рассматривания Option-а, как списка с размером [0..1]?
Идею видел, но не размышлял вдумчиво о плюсах и минусах такого подхода. Математически идея красивая. Монады на ней же строятся.Со здравым смыслом у меня пока не бьется, но это может быть из-за новизны рассмотрения.
Корни решения растут из рассматривания Option-а, как списка с размером [0..1]?Корни решения растут из рассматривания Option-а как функтора.
В C# этот вопрос уже решили за тебя, иначе ты не сможешь пользоваться monadic comprehension синтаксисом.
Корни решения растут из рассматривания Option-а как функтора.Из этого утверждения не следует, каким будет поведения flatmap-а для Option-а. Соответственно, оно не является ответом на мой вопрос.
ты не сможешь пользоваться monadic comprehension синтаксисом.не использую эту ересь! и другим не советую! )
Из этого утверждения не следует, каким будет поведения flatmap-а для Option-а. Соответственно, оно не является ответом на мой вопрос.Согласен. Необходимо дополнительное предположение о эквивалентности Option списку из не более чем одного элемента
ifSet
не использую эту ересь! и другим не советую! )ну и дурак
Почему 'set'? Это или "установить значение", или "множество". Ни то, ни другое у меня не соединяется с задачей.
ну и дуракВ хаскелле вроде do-нотация тоже не тру считается
Это сокращение от ifValueIsSet
В хаскелле вроде do-нотация тоже не тру считаетсяГде хаскель, а где мы.
В java есть ifPresent. Ну, и собственно, map...
Это сокращение от ifValueIsSetкучеряво ) Мозг сломаешь, пока уложишь сокращение с полным названием и смыслом )
В Rust это называется and_then (есть и парная к ней or_else).
кучеряво ) Мозг сломаешь, пока уложишь сокращение с полным названием и смыслом )Upgrade your brain.
Чем тебе вполне монадный (f)map не нравится?
Да,
flatMap :: (a -> Option b) -> Option a -> Option b
flatMap _ Null = Null
flatMap f x = f x
А чтоб не совпадало с определением fmapа, надо строго боксить. В таком виде в твоем Option смешаны в кучу Null и _|_.
Чем тебе вполне монадный (f)map не нравится?Название функции не совпадает с той целью, которая ставится при вызове этой функции.
Flat-овость - это особенность реализации Option-а, а не смысл операции.
Название функции не совпадает с той целью, которая ставится при вызове этой функции.?
Как называется парная операция к fmap? Которая выполняет операцию на Null, но не выполняет над не-Null.
Ей нужно даже какое-то специальное название?
ifNull :: b -> Maybe a -> Maybe b
ifNull b Nothing = Just b
ifNull _ _ = Nothing
Которая выполняет операцию на Null, но не выполняет над не-Null.
random
Не учитывает случай детерминированных операций над Null-ом.
У flatmap частичная сигнатура (рассматривая кол-ва): many<many<item>> -> many<item>, у обсуждаемой операции: item{0..1} -> item{0..1}
Константы можно выкинуть не ограничивая общности.
Константы можно выкинуть не ограничивая общности.Не учитывает случая, когда операция детерминированная, но не константная.
Приведи пример.
string display(item, точность, settings) =>
item.weight.to-string(точность ? settings.точность)
? - синоним операции, которая выполняет операцию над Null, но не выполняет над не-Null
Как ты ловко добавил контекст. Я так понимаю, что конеткст несложными преобразованиями можно перевети в параметры функции и тогда это уже не будет операция над нул.
Я так понимаю, что конеткст несложными преобразованиями можно перевети в параметры функции и тогда это уже не будет операция над нул.Да, замыкание можно развернуть. Но зачем? Как это упростит код?
Название функции не совпадает с той целью, которая ставится при вызове этой функции.Задача 1: получить все сообщения из тредов на текущей странице
решение в лоб: current-page.threads.map(.messages Результат: списки списков вместо списка. Разница между получаемым и ожидаемым в уровнях списков. Закономерное решение использовать flatmap.
Задача 2. скачать аватарку по текущему сообщению.
решение в лоб: current-message.avatar.download. Результат: выдает ошибку рантайма или компиляции: аргумент ожидается, а его - нет. Нет несовпадения уровней, flatmap выпрыгивает как рояль из кустов. Сформулированная семантика уточненного решения не даёт отсылку к преобразованию уровней: обрабатывать ситуации с отсутствующим входным параметром особым образом.
Maybe(Nothing, Just
maybe,
Either(Left, Right
either,
то есть можно назвать твою функцию тупо option.
either что делает?
http://www.haskell.org/onlinereport/haskell2010/haskellch9....
either :: (a -> c) -> (b -> c) -> Either a b -> c
either f g (Left x) = f x
either f g (Right y) = g y
Да, замыкание можно развернуть. Но зачем? Как это упростит код?Делать этого незачем. Код никак не упростит. Однако, это позволяет понять, что ты как-то особенно трактуешь операцию над нул.
решение в лоб: current-message.avatar.download. Результат: выдает ошибку рантайма или компиляцииТвой лоб выдает ошибки. Ты решил поделиться этим с форумом?
Делать этого незачем. Код никак не упростит. Однако, это позволяет понять, что ты как-то особенно трактуешь операцию над нул.Покажи, пожалуйста, это на примере
either :: (a -> c) -> (b -> c) -> Either a b -> cEither - это маркер, помогающий преобразовать целое, описывая операции лишь над частью.
Так?
Что операция над нул, если ты еще включаешь в рассмотрение некоторый конеткст, становится операцией над этим конекстом?
По-моему, это очевидно без примера.
По-моему, это очевидно без примера.Неочевидно "Однако, это позволяет понять, что ты как-то особенно трактуешь операцию над нул."
Вот с этим ты согласен?
операция над нул, если ты еще включаешь в рассмотрение некоторый конеткст, становится операцией над этим конекстом
да
Тогда мне кажется очевидным, что "чистая" операция над нул может быть либо константа либо random. С чего я и начал.
Тогда мне кажется очевидным, что "чистая" операция над нул может быть либо константа либо random. С чего я и начал.Очевидно: только Null никогда не бывает.
Всегда есть контекст: Null + f
Генеренная в runtime функция без побочных эффектов - является ли чистой операцией?
Которая выполняет операцию на Null, но не выполняет над не-Null.
Корректное утверждение в этой логике:
Которая выполняет операцию на Null внутри контекста, но не выполняет над не-Null внутри контекста.
ps
hints:
В чём отличие Either-типа от произвольного tagged union-типа?
В каких задачах стоит использовать Either?
> В каких задачах стоит использовать Either?
В задачах, в которых лень заводить отдельный tagged union-тип? В Left можно например положить успешный результат функции, а в Right исключение. Ну это так, пальцем в небо, я не программирую на хаскеле промышленно. Но вообще твой вопрос из разряда зачем нужен std::pair когда можно всегда написать свой собственный struct.
Ну кстати да, погрепал стандартную библиотеку, так его там и используют
try :: IO a -> IO (Either Exception a)
tryJust :: (Exception -> Maybe b) -> IO a -> IO (Either b a)
и т.д.
http://danielwestheide.com/blog/2013/01/02/the-neophytes-gui...
В ней утверждается, что map и flatmap для Either перегружены особым способом, в отличии от tagged union.
Соответственно, тезис "Either используется в задачах, в которых лень заводить отдельный tagged union-тип" - слабый, из-за отсутствия эквивалентности между Either и tagged union.
std::pair и struct имеют эквивалентные операции.
За основу тезиса "Either - маркер, помогающий преобразовать целое, описывая операции лишь над частью" взята статья: В ней утверждается, что map и flatmap для Either перегружены особым способом, в отличии от tagged union.
Соответственно, тезис "Either используется в задачах, в которых лень заводить отдельный tagged union-тип" - слабый, из-за отсутствия эквивалентности между Either и tagged union.
std::pair и struct имеют эквивалентные операции.
Столько cs философии на пустом месте, у меня аж голова кружится. Короче, в хаскельном Either нет этих "проекций", это самый обычный tagged union. either это его fold. fmap не определен.
Я тут правда понял, что хаскельное maybe это тоже fold, а Not-Null с которого всё началось, это таки map. Так что так его и надо называть.
Короче, в хаскельном Either нет этих "проекций", это самый обычный tagged union. either это его fold.Фу! Как скучно! )
Согласен, в haskell - это просто tagged union для ленивых.
ps
Either в scala-е интереснее, помогает делать сложные преобразования простым образом.
Фу! Как скучно! )Для оживления дискуссии предлагаю обсудить disjoint union. Tagged union, он же обыкновенная сумма типов, нуден тем, что его каждый раз надо конструировать и раскрывать обратно. А disjoint union - это возможность просто объявить супертип, который просто представляется либо одним, либо другим.
В скале есть костыль для этого
Но он не слишком удобный. Лучше бы это была фишка языка. Вот есть, например, мультинаследование, можно объявить тип наследником нескольких других. type child extends c1 & c2 & c3 . В любой точке, где ожидается тип с1, можно использовать child взамен его. А также взамен c2 и c3.
А иногда очень не хватает обратной конструкции type base supersede c1 | c2 | c3. И тогда в любом месте кода, где ожидается base, можно было бы использовать на выбор c1, c2, c3. Вот, например, построена у тебя система на обработке событий. ClassA умеет делать process(event : EventA), ClassB умеет делать process(event : EventB). А тебе хочется занаследоваться от обоих, тогда тебе нужно уметь делать process(event : EventA | EventB). А я не знаю язык, в котором такая конструкция типов поддерживалась бы. Программисты скалы страдают и использует вместо строгой типизации (Event1, Event2, Event1 | Event2) просто Object. Программисты джавы не страдают, они с самого рождения только Objectами и пользуются, про типобезопасность не слышали.
А есть языки, где это реализовано из коробки?
supersede c1 | c2 | c2 <=> supersede (supersede c1 | c2) | c3 <=> supersede c1 | (supersede c2 | c3) <=> supersede c2 | (supersede c3 | c1)
Делается через нормализацию supersede-типа.
Экспериментировал с этой концепцией в своем язычке. До релиза не довёл.
Еще хочется, чтобы поддерживалась эквивалентность:именно. Поэтому мне костыль для скалы и не понравился, он ненормализованный
supersede c1 | c2 | c2 <=> supersede (supersede c1 | c2) | c3 <=> supersede c1 | (supersede c2 | c3) <=> supersede c2 | (supersede c3 | c1)
Для нормализации требуется полноценный язык с операциями декомпозиции, сортировки и т.д. Такой язык - или не строится поверх используемой системы типов, или строится с огромным оверхедом (по объему кода и производительности компиляции).
Ну я понимаю, что внутри скалы я это не сделаю. Как и вообще внутри jvm. Но по идее это можно сделать на уровне отдельного языка. Вдруг кто-то заморочился?
Ну я понимаю, что внутри скалы я это не сделаю.Практически или теоретически?
Scala-у плохо чувствую, но предполагаю, что теоретически сделать можно. Точно можно сделать на template-ах C++, но это будет здоровый монстр.
А так implicit нельзя запускать по цепочке. А на них строилось то решение, которое ненормализованное. Для нормализации потребуется рекурсивное implicitное преобразование, а такого нельзя.
А так implicit нельзя запускать по цепочке.да, это часто мешает.
Для нормализации потребуется рекурсивное implicitное преобразованиеКак вариант, сделать через явный вызов преобразования. Для этого требуется функция, выполняемая на уровне системы типов, с сигнатурой: flat_supersede<T1,..TN> Normalize(recursive_supersede<..>)
На template-ах C++ вид вызова: supersede<bla-bla>::normalized.
Tagged union, он же обыкновенная сумма типов, нуден тем, что его каждый раз надо конструировать и раскрывать обратно. А disjoint union - это возможность просто объявить супертип, который просто представляется либо одним, либо другим.
...
А есть языки, где это реализовано из коробки?
См. union types в Ceylon:
http://ceylon-lang.org/
А другая твоя хотелка - это intersection types из того же Ceylon'a. Уже все сделали.
Фу! Как скучно! )Тебя обманывают, а ты не верь.
Согласен, в haskell - это просто tagged union для ленивых.
ps
Either в scala-е интереснее, помогает делать сложные преобразования простым образом.
В хаскеле "Either e" это монада (и функтор, само собой поэтому имеет и fmap и flatMap (в хаскельном его написании) и поддержку do-нотации (которая вместо скального for'a). Работает как RightProjection в скале, т.е. маппит значения Right.
"unbiased" как раз означает, что "главной" проекции не зафиксировано. А так получается, что Either в хаскеле работает как Try в скале. И используется так же, исключения пробрасываются через Left, а успехи обрабатываются с помощью fmap над Right.
Ну ок, это не то же самое, что "Either это обычный tagged union", согласен
Работает как RightProjection в скале, т.е. маппит значения Right.Итоговая коллекция будет содержать как модифицированные Right, так и исходные Left?
data Either a b = Left a | Right b
deriving (Eq, Ord, Read, Show, Typeable)
instance Functor (Either a) where
fmap _ (Left x) = Left x
fmap f (Right y) = Right (f y)
instance Monad (Either e) where
return = Right
Left l >>= _ = Left l
Right r >>= k = k r
Осталось дождаться, когда там паттерн матчинг изобретут. Потому что делать юнион тип и проверять все варианты через if - это извращение.
Оставить комментарий
Dasar
Есть какое-нибудь устоявшееся название для операции, которая выше обозначена как Not-Null? (Для Null возвращает Null, а для не-Null применяет переданную функцию.)
Какое слово можно взять в качестве такого названия?
Хочется что-то короткое (3-4 буквы в одно английское слово.