5 ms·
These languages use weaker monad syntax because of the difficulty conceptualizing programming in a way other than "Do arbitrary action A to object B in scope C"
by ipnon 7y ago
These languages use weaker monad syntax because of the difficulty conceptualizing programming in a way other than "Do arbitrary action A to object B in scope C". You can't gain from a more powerful monad abstraction without losing the constraints of impure functions, dynamic typing, and strict evaluation.
It is therefore incumbent upon Haskellers to make the superiority of powerful abstractions like the monad abundantly clear through the propagation of high-quality software using said abstraction. :)
- whateveracct 7y agoScala has monad abstractions (and even special syntax for it) without enforcing purity or having lazy evaluation by default.
- pdpi 7y agoThe real difficulty with having “proper” support for monads is having a type system that supports higher-kinded types of some description
- quelltext 7y agoOr alternatively no (static) type system at all.
- lmm 7y ago> You can't gain from a more powerful monad abstraction without losing the constraints of impure functions, dynamic typing, and strict evaluation. Those languages that choose to use options or results with ?. instead of null or exceptions are already choosing to avoid impure functions and use static typing. Monads work fine with strict evaluation (indeed we see post-Haskell languages e.g. Idris using a strict evaluation model).
- mumblemumble 7y agoMonads do work fine in languages with strict evaluation. In and of itself, that isn't really any more compelling a statement than observing that the strategy pattern works fine in languages with first-class functions. Just because you can do something in a certain way doesn't mean it's your best option. The argument's got to go a bit further than that.
- lmm 7y agoAre you suggesting that languages with strict evaluation have some better alternative to monads? Because that's simply not true.
- mumblemumble 7y agoTalking specifically about strict evaluation: Monads rose to prominence in Haskell in order to deal with some specific technical challenges that stemmed from the project's decision not to make any compromises about lazy evaluation. Using monads for specifically that purpose in a language with strict evaluation would be a case of cargo culting Haskell. That is certainly not the only use for monads. Haskell uses them as a general-purpose solution for things that other languages typically handle with an absolute zoo of special-purpose features. In that respect, you could argue that monads are, at least in the abstract, better than all those features, because they're one thing that can do the work of many. However, if you're working in a language that already has some mix of strict evaluation, mutable variables, async/await keywords, exceptions, elvis operators, null punning, etc., introducing monads to solve those problems runs the real risk of running afoul of the xkcd.com/927 problem. Especially if those existing language have a tendency to not understand and respect the fact that they're now being used in code that's supposed to be observing the monad laws. Even implementing `Maybe` in a language with nulls can be surprisingly tricky to get right. Which is where I stop having much interest in holy wars about this stuff. If you're lucky enough to be working in Haskell or a language inspired by it, that's awesome, you've got a whole menagerie of category theoretic abstractions to work with, and a language that's actually designed to help you use them with confidence. If you're not, then I'd argue that their use needs to be justified in much more specific, pragmatic terms, including an eyes-wide-open perspective on what the ergonomics and technical challenges are going to be like. Because, believe me, it's no fun having to work with a Try monad that can both return and throw errors.
- lmm 7y ago> Monads rose to prominence in Haskell in order to deal with some specific technical challenges that stemmed from the project's decision not to make any compromises about lazy evaluation. Disagree. To an outsider, IO is the most prominent monad because it's very visible in small example programs (and something that separates the Haskell versions of those small example programs from those in other languages). But to a practitioner it's not a particularly important or interesting example, most use of the monad abstraction is not about IO, and I would certainly hope that the language (in the broad sense) would have adopted it with or without IO. > Using monads for specifically that purpose in a language with strict evaluation would be a case of cargo culting Haskell. Disagree; even in languages which define an evaluation order, it's still very much implicit to a human reader. Being able to distinguish between accidental ordering and intentional ordering is really helpful to enable fearless refactoring. Like I said, post-Haskell languages like Idris often use strict evaluation but still see value in managing IO explicitly. > However, if you're working in a language that already has some mix of strict evaluation, mutable variables, async/await keywords, exceptions, elvis operators, null punning, etc., introducing monads to solve those problems runs the real risk of running afoul of the xkcd.com/927 problem. The special-case syntax is often more suitable to the special cases it was designed for (it would be pretty tragic if it weren't). But the general-case syntax is necessary for general-case code. It can work pretty nicely as long as there's a direct transformation between one and the other; compare e.g. different ways of iterating through a collection, where you'll generally have a fully general function/syntax but also sugar that's more appropriate to restricted special cases. > Especially if those existing language have a tendency to not understand and respect the fact that they're now being used in code that's supposed to be observing the monad laws. I'd argue that if your special-case solution doesn't conform to the monad laws then you already have a problem, even if you don't realise it yet. E.g. the fact that in many languages code that uses a map will break if that map contains null values, and the fact that implementing a valid maybe monad on top of null is difficult-to-impossible, are both reflections of the same underlying problem.
- nybble41 7y ago> Those languages that choose to use options or results with ?. instead of null or exceptions are already choosing to avoid impure functions and use static typing. An impure "function" is any procedure which may have side-effects or produce different results for the same formal arguments depending on the runtime environment or evaluation order. Most languages with option or result types (e.g. Rust) still make extensive use of impure procedures. Haskell and its close relatives are pretty much the only well-known exceptions with first-class pure functions enforced through the type system.
- lmm 7y agoPurity is always a spectrum - Haskell allows non-terminating "functions", for example. Choosing to treat certain kinds of failures as valid function evaluations that can be reasoned about under the normal rules of the language is a step in the direction of purity. More to the point, it's the aspect of purity that's salient when we're talking about whether it makes sense to regard these particular constructs as monads.
- nybble41 7y ago> Haskell allows non-terminating "functions", for example. Non-terminating functions are still pure functions in the mathematical sense. While it does somewhat complicate the use of programs as proofs, a pure function doesn't need to have a defined value for every possible input. A better example might have been unsafePerformIO which, like "unsafe" in Rust, is meant to be used to construct a pure interface to impure code but depends on the programmer to handle it properly. The difference is that Rust doesn't require "unsafe" around all side-effects, just those that may impact memory safety. I would agree that Option and Result types represent "a step in the direction of purity", but even a little bit of impurity nullifies referential transparency and inhibits equational reasoning. > ... it's the aspect of purity that's salient when we're talking about whether it makes sense to regard these particular constructs as monads. A construct is a monad if it obeys the monad laws for all well-typed inputs. In languages like Rust or JS which lack any type-level enforcement of purity the constructs are only monads under the condition that the inputs happen to be pure, not in the general case. For example, for any functor (which includes all monads) we have the law "map f . map g == map (f . g)". However, if f and g may have side effects then substituting one side for the other will interleave the effects in a different order and potentially change the result, so the monad laws are not satisfied.
- LessDmesg 7y agoMonads are not a superior abstraction. Their composition is not commutative and, worse, not even closed (a composition of two monads may not be a monad). Monad transformers are clumsy syntax-wise and have a performance penalty. When I used Haskell, monads were the most meh part: not a pain, but not anything too cool either. Just some syntax sugar to remove a repetitive wrapping function from a few lines of code.