Здравствуйте, Sinclair, Вы писали:
G>>Почему только при присвоении?! Везде, где "по смыслу" должно быть T. S>Везде, где по смыслу должно быть T, должен быть лифтинг. S>Монада — она на то и монада.
Так-то да. Но, там речь даже не о типо-maybe шла... А о неком variant[T], который не монада, а есть "упращение" от variant[T, U] при U = T. В это предлагается вычислять if-с-else.
---
С уважением, Константин Сиваков
Re[7]: Nemerle через 5 лет - выстрелит или скончается?
Здравствуйте, Sinclair, Вы писали:
S>А зачем вы обязательно хотите одинаковости в вычислениях if-с-else и if-без-else? S>На мой взгляд, это два разных оператора, которые вполне имеют право возвращать разные результаты.
Я-то как раз никакой одинаковости и не "хочу". Это действительно два разных оператора, и то, что в сабже у них и синтаксис разный — это даже плюс, имхо.
S>Можно вообще считать else шорткатом для оператора ??. S>То есть результат выражения if (x == 5) 42 имеет тип Nullable<int>. S>Результат выражения if (x == 5) 42 else 41 имеет тот же тип, что и someNullableInt ?? 41, т.е. просто int.
Соглашусь, только если не Nullable<int>, а честный maybe<int>. Nullable, в этом случае, такой же паллиатив как и variant[int, void]
---
С уважением, Константин Сиваков
Re[16]: Nemerle через 5 лет - выстрелит или скончается?
Здравствуйте, gbear, Вы писали:
G>Вот, при вычислении мы получили некий variant[int, void]. Вы, только честно, способны сказать _что_ мы получили? Ваше типо-maybe? Или, может быть, это "нормальный" variant?
А чем он не нормальный?
G>Вы предлагаете интерпритировать _любой_ variant[T, void] как maybe чтоли?! А если T — внезапно — окажется void? if(x > 0) foo(x) — это тогда "go wrong" — без вариантов
Почему go wrong? После упрощения будет просто void.
Если же хочется большой verbose многоэтажности, то можно делать: variant[just[T], nothing], для нескольких веток — variant[just[T], just[U], nothing] (или как вариант — variant[variant[T, U], nothing]), если есть else — то убрать nothing.
G>А что делать в "обобщенном коде", когда любой variant[T, U], при таком подходе, способен внезапно стать таким типо-maybe? G>Еще раз... maybe — вещь вполне конкретная. Обладает вполне определенной семантикой. variant — такой семантической нагрузки не несет. И "усилием воли" её на него "повесить" не выйдет.
Допустим что у maybe есть какая-то специальная maybe-семантика, которая с variant'ом не вяжется ну никак. Ок, просто не будем называть Variant[T, void] maybe — делов-то
G>>>Есть foo(x) -> void и bar(x) -> variant[int, void]. G>>>
G>>>if(x > 0) bar(x) else foo(x)
G>>>
G>>>Нужно "дергать"? EP>>Если неявного упрощения нет — то да, можно. G>Кхм... я дико извиняюсь. Но я таки спрашивал вас не за "можно", а за "нужно".
Нужно или нет зависит от конкретной ситуации. Вполне возможно что в обобщённом коде понадобится не упрощённая версия:
auto first = bar(1);
// ...auto v = if(x > 0) bar(2) else foo(x);
match(v) case(v is typeof(bar(1))) { first = v; } // ...
В то же время можно представить ситуацию, когда нужна именно упрощённая версия — например return simplify(if(x > 0) bar(2) else foo(x));.
Именно поэтому "можно", а не "нужно".
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>А почему void, а не Maybe?
Тогда уж if без else должен возвращать Alternative f => f a, а уж никак не Maybe.
Но в nemerle нет higher-kinded polymorphism, поэтому сделать так нельзя.
Версия с void вполне имеет право на существование, у нее другие юзкейсы, в том же хаскеле (теперь) есть
guard :: Alternative f => Bool -> f ()
when :: Applicative f => Bool -> f () -> f ()
unless :: Applicative f => Bool -> f () -> f ()
Правда, в хаскеле when более общая функция, чем guard, про обсуждаемую "версию с void" такого не скажешь.
(до AMP вместо Alternative был MonadPlus, а вместо Applicative — Monad, в контексте разговора это не интересно, Monad для такой функции не требуется, а MonadPlus исключительно продукт недоделанности хаскельной библиотеки, который в других языках повторять не нужно)
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Здравствуйте, Evgeny.Panasyuk, Вы писали: EP>Опять же, это можно делать одним явным вызовов унарного оператора, который по-максимуму упростит тип.
Этот унарный оператор нужно будет постоянно втыкать к месту и не к месту.
Зачем он нужен? У нас есть реальная потребность оперировать вложенными вариантами?
EP>Основной вопрос в том, сколько неявности можно себе позволить, не сломав при этом обобщённый код (в который могут прийти и типы приводящие к variant'у который можно упростить и те для которых его упростить нельзя).
Я пока затрудняюсь представить себе такой сценарий, в котором обобщённый код сломается из-за автоупрощения. Вы можете представить себе такой код?
EP>Опять таки, возникает вопрос совместимости с обобщённым кодом: if(x == 5) T() else U() — тут T и U могут быть одинаковые или разные.
Ну да. Если T и U одинаковые, то в одном из вариантов языка мы имеем T благодаря автоупрощению, в другом — Object, если нет ограничений на T и U.
Ну или C, если в декларации параметров T и U указано where T:C, U:C.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, gbear, Вы писали: G>Так-то да. Но, там речь даже не о типо-maybe шла... А о неком variant[T], который не монада, а есть "упращение" от variant[T, U] при U = T. В это предлагается вычислять if-с-else.
Упрощением от variant<T, U> для T=U должен быть просто T.
Потому что "int или int" — это просто int.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>А почему void, а не Maybe?
Потому что Maybe тут никому не нужен. Еще потому что внутри when может быть return/break/continue/возврат из именованного блока. Ну, и с точки зрения производительности выделение памяти под Maybe не комильфо. Кому надо заманадить, может воспользоваться комьютейшон экспрешонами, где все можно определить так как хочется.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, gbear, Вы писали: G>Вот, при вычислении мы получили некий variant[int, void]. Вы, только честно, способны сказать _что_ мы получили? Ваше типо-maybe? Или, может быть, это "нормальный" variant? G>Вы предлагаете интерпритировать _любой_ variant[T, void] как maybe чтоли?! А если T — внезапно — окажется void? if(x > 0) foo(x) — это тогда "go wrong" — без вариантов
Упрощение variant[T, T] должно давать T для любого T. В том числе и для void. Так что (if(x > 0) foo(x)) будет иметь тип void, что вполне совпадает с интуитивными ожиданиями.
G>А что делать в "обобщенном коде", когда любой variant[T, U], при таком подходе, способен внезапно стать таким типо-maybe?
А в чём тут проблема, напомните?
G>Еще раз... maybe — вещь вполне конкретная. Обладает вполне определенной семантикой. variant — такой семантической нагрузки не несет. И "усилием воли" её на него "повесить" не выйдет.
Я вижу у maybe только одно отличие от абстрактного варианта — это наличие доп. синтаксиса с hasValue.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, gbear, Вы писали: G>>Так-то да. Но, там речь даже не о типо-maybe шла... А о неком variant[T], который не монада, а есть "упращение" от variant[T, U] при U = T. В это предлагается вычислять if-с-else. S>Упрощением от variant<T, U> для T=U должен быть просто T. S>Потому что "int или int" — это просто int.
В "два" как раз есть случай помянутого выше упрощения для альтернативного парсера a | b (т.е. if/else по сути):
a: A, b: B --> (a | b): variant<A, B>
a: A, b: Unused --> (a | b): optional<A>
a: A, b: B, c: Unused --> (a | b | c): optional<variant<A, B> >
a: Unused, b: B --> (a | b): optional<B>
a: Unused, b: Unused --> (a | b): Unused
a: A, b: A --> (a | b): A
тут a, b — это выражения (парсеры), а A, B — их типы (типы их атрибутов), optional — это Maybe, Unused — void.
Здравствуйте, Sinclair, Вы писали:
S>Упрощение variant[T, T] должно давать T для любого T. В том числе и для void. Так что (if(x > 0) foo(x)) будет иметь тип void, что вполне совпадает с интуитивными ожиданиями.
В этом разговоре как-то смешиваются типы-объединения с типами-суммами. Да T V T — это то же самое, что и T. Но T + T — не то же самое, что T.
Упрощенно говоря T + T = Left * T V Right * T, мы всегда можем отличить значение производимое injl от injr по "тегу".
Суммы, они же алгебраические типы и "варианты" — вещь распространенная, полезная на практике. Объединения, с другой стороны, штука довольно сомнительная и редко встречающаяся.
Что из себя представляет boost variant я вообще не смогу навскидку сказать, к примеру, он описывается как "set of types", и "static visitor" различает элементы по типу. Т.е. это никакой не вариант (не тип-сумма). Но с другой стороны это и не объединение, потому что мы не можем просто применять к значению этого типа функции, определенные на всех его элементах без приседаний со всякими визиторами. Т.е. он, похоже, совмещает в себе недостатки сумм и объединений (не могу сказать, что это неожиданный итог, раз уж речь идет о какой-то C++-поделке).
Действительно, с помощью объединений можно типизировать if таким образом:
Γ |- t1 : T1, T1 = Bool, Γ |- t2 : T2, Г |- t3 : T3, T2 V T3 = T
----------------------------------------------------------------
Γ |- if t1 then t2 else t3 : T
а if без else как T2 V Unit, но это полностью бессмысленно, потому что на объединении безопасны только операции, которые определены на всех элементах объединения, а на Unit (который тут void называют) ничего интересного не определить. Т.е. никакой практической разницы с возвращением просто Unit нет.
Сумму же можно элиминировать паттерн-матчингом (благодаря тегам) и обработать каждый элемент суммы теми операциями, которые на этом элементе определены.
G>>А что делать в "обобщенном коде", когда любой variant[T, U], при таком подходе, способен внезапно стать таким типо-maybe? S>А в чём тут проблема, напомните?
S>Я вижу у maybe только одно отличие от абстрактного варианта — это наличие доп. синтаксиса с hasValue.
Не понял, что за синтаксис. Maybe T — это сумма типа T и единицы.
То, что тут проходит под названием "типа Maybe" — это Alternative.
Если обобщеный "if без else" типизировать как Alternative f => Bool -> a -> f a то он будет работать с любым "типа Maybe", хоть с Maybe, хоть со списком, хоть с суммой типа с чем-то являющимся моноидом, т.е. в том числе и для суммы типа с единицей — Either () a.
Полиморфизм тут облегчает дизайнерское решение с выборами конкретного "типа Maybe" просто устраняя необходимость выбора: все что работает как Maybe будет работать с таким "if без else"
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll