[HN Gopher] What Color Is Your Monad
___________________________________________________________________
What Color Is Your Monad
Author : patrickthebold
Score : 76 points
Date : 2022-02-14 11:49 UTC (2 days ago)
(HTM) web link (patrickthebold.github.io)
(TXT) w3m dump (patrickthebold.github.io)
| continuational wrote:
| I'm working on a web language with colorless async/await:
| https://www.ahnfelt.net/async-await-inference-in-firefly/
| kazinator wrote:
| In colorForth, words are actually colored and that has semantics.
|
| https://en.wikipedia.org/wiki/ColorForth
| jmmv wrote:
| " I'd be interested in a language that forces the use of
| Optional, Either instead of allowing for null, exceptions. Maybe
| Haskell does this?!"
|
| Well, Rust does.
| gowld wrote:
| http://go/rs:std::ptr::null Function
| std::ptr::nullCopy pub const fn null<T>() -> *const
| T
|
| > Creates a null raw pointer.
| paoda wrote:
| Perhaps "Safe Rust" would be more apt (de-referencing any
| pointer is unsafe), though I think it's quite fine and well
| accepted to refer to "Safe Rust" as just "Rust".
|
| Of course, the null pointer can still _exist_ in Safe Rust,
| but since you can 't de-reference it I think it's fine to say
| Safe Rust is "close enough" to count.
| phpnode wrote:
| You can only dereference this pointer if you work at Google!
| pwdisswordfish9 wrote:
| Wait, there are non-Googlers here? Who let them in?
| steinuil wrote:
| You can only dereference raw pointers from within an unsafe
| block so they're only useful inside unsafe blocks. IMO Unsafe
| Rust != Rust, unsafe is an extension to the language that is
| clearly separated from safe code, and using it essentially
| means voiding Rust's safety contract.
| dgellow wrote:
| Not that you're incorrect but to be even more precise, and
| for the readers who do not know about Rust, I would say
| "voiding *parts* of Rust's safety contract".
|
| Citing the documentation:
|
| > To switch to unsafe Rust, use the unsafe keyword and then
| start a new block that holds the unsafe code. You can take
| five actions in unsafe Rust, called unsafe superpowers,
| that you can't in safe Rust. [...]:
|
| > - Dereference a raw pointer
|
| > - Call an unsafe function or method
|
| > - Access or modify a mutable static variable
|
| > - Implement an unsafe trait
|
| > - Access fields of unions
|
| > It's important to understand that unsafe doesn't turn off
| the borrow checker or disable any other of Rust's safety
| checks
|
| https://doc.rust-lang.org/book/ch19-01-unsafe-
| rust.html#unsa...
| steveklabnik wrote:
| I'm not even sure that captures it. You must not void the
| safety contract in unsafe Rust. The distinction is that
| with unsafe, you must _manually_ uphold the safety
| contract.
| dgellow wrote:
| Ah yes, you're right that I missed that part. The
| contract isn't voided by using unsafe, what changes is
| that responsibility of enforcing the contract is moved to
| the developer instead of the compiler.
| lowbloodsugar wrote:
| Once had a problem where customers complained about an app
| keep crashing and losing minutes or hours of work. Boiled
| down to a "null reference" in C++. Programmer responsible
| argued till blue in the face that his code could not have a
| bug because references (&) _cannot be null_. Of course they
| can.
|
| Rust, at least, requires that this happens in unsafe code,
| but for sure, a bug in unsafe code can lead to null
| references. We are doing noobs a disservice if we claim
| otherwise.
| cbarrick wrote:
| https://doc.rust-lang.org/std/ptr/fn.null.html
| chriswarbo wrote:
| Haskell doesn't have null, so Optional is the usual alternative
| (actually called "Maybe" in Haskell, but it's the same thing)
|
| Haskell values can throw exceptions (e.g. for division-by-zero,
| etc.); but _catching them_ (and therefore branching on them) is
| an IO effect.
|
| That sort of exception is treated like an unfortunate necessity
| in Haskell; and user code tends to use a more structured, well-
| typed approach like Either (there are a few alternatives
| floating around in common libraries)
| amelius wrote:
| Can we have an editor which uses display colors based on what
| color a function is? Or monad, of course.
| eyelidlessness wrote:
| I think this would probably be trivial with TextMate themes
| (used by VSCode, Sublime, and lots of other editors). If not,
| certainly it's possible with a quick tree-sitter query. The
| latter also presents a wildly expansive set of options for
| specialized static analysis (eg I built a naive TypeScript
| "compiler" with it in about a day).
| WorldMaker wrote:
| > Javascript, of course, has async/await syntax. I'm curious if
| that is equivalent to for expressions/do notation and could (in
| theory) be used for monads other than Promise
|
| It's very similar indeed to do-notation.
|
| The contract for JS async/await is really simple, too, it
| basically supports any "Thenable" object: an object with a
| monadic bind function called .then(). It's basically "duck typed"
| in JS, if it has a .then() function that takes the right sort of
| callback you can await it.
|
| I haven't seen any practical uses of async/await syntax for
| monads other than Promise, but it's very doable in theory.
| draw_down wrote:
| jeremyjh wrote:
| One difference with monads at least in Haskell is we have the
| concepts of lifting a computation in one type to a type of
| another (Monad Transformers), and then dropping those
| transformations to a base type (such as IO) via monad-control.
| This lets you pretty easily call functions of different colors
| without a lot of manual wrapping and unwrapping. To make it super
| convenient you do need to use a lifted version of the base
| library, which is sort of like a color-agnostic set of IO
| functions that can be directly called from any transformer stack
| that implements the proper typeclasses.
|
| https://hackage.haskell.org/package/monad-control
|
| https://hackage.haskell.org/package/lifted-base
| Akronymus wrote:
| Isn't that a direct result of having higher kinded
| types/abstracting over the type constructor?
| jeremyjh wrote:
| Certainly but Scala has that, and it has typeclass like
| implicits. I'm not sure if it can do monad-control though,
| here is a list of all the type extensions used for that:
| RankNTypes , TypeFamilies ,
| FunctionalDependencies , FlexibleInstances
| , UndecidableInstances , MultiParamTypeClasses
| Akronymus wrote:
| One day ill make the jump from "just" f# to haskell.
|
| While i usually could solve it in a different way, such
| things would have made it quite a bit nicer (the zoo of
| .map, .fold and such is a bit annoying)
| isaacimagine wrote:
| I've been waiting for this piece for a while. Monads don't
| compose!
|
| Under an effect-based system: fn get_user<F>()
| -> F<User>
|
| Is like having an explicit effect F: fn
| get_user<F>() ->{F} User
|
| Where {F} is the effect set.
|
| Note that User is no longer wrapped in F, and that we can extend
| the effect set (e.g. {F, G, H})
|
| Consider a function that is async and throws an error. Is this:
| Promise<Exn<T>>
|
| Or: Exn<Promise<T>>
|
| Convention (and common sense) dictates the former, but in the
| face of many effects, who's to say there even is an order at all?
|
| By using an effect set, we get: {Promise, Exn}
| T
|
| And we can even ensure that our function is generic over
| additional effects: {Promise, Exn, F} T
|
| Individual monads compose, but multiple requires explicit
| composition. By modeling monadic effects using the free monad
| (i.e. effect sets), we can circumvent the colored function
| problem entirely: functions can have different colors, and be
| generic over any other color it might come in contact with.
|
| Cool beans!
| iamwil wrote:
| Have you used Algebraic Effects a lot? What are the downsides
| to using them in your experience, if you have?
| isaacimagine wrote:
| I have some experience with effects, mostly from noodling
| around with Koka and implementing a small effect-based toy
| language. (Which I intend to integrate into a larger language
| I'm working on.)
|
| There are a few downsides, and they fall into four
| categories:
|
| 1. Effect sets get rid of external effect ordering. If you
| have an effect set {A, B}, you can't know if A happens before
| B without reading the code. Same issue as monad transformers.
|
| 2. Handlers are sometimes hard to reason about. A handler is
| essentially a fancy try/catch block for resumable exceptions.
| And construct that introduces a non-local control-flow
| dependency can be hard to reason about. (This is especially
| true as a compiler writer, haha)
|
| 3. The syntax is hard. I'm serious: how do you even represent
| an effect set? I went with a unison-like approach in my post,
| but this breaks down in an unsatisfactory if you make some
| other decisions. Also, what is an effect set in relation to
| your languages type system? Is it just a fancy enum? Should
| we allow other sets of types? What does an effect set mean on
| its own?
|
| 4. Lack of idioms and libraries. Result<T, E> is well-
| established - what idioms arise when programming with
| effects? In my experience, effects are rarely explicit, and
| are only used if you're trying to implement something using
| non-trivial control flow, like generators or cancellation.
| dwohnitmok wrote:
| > If you have an effect set {A, B}, you can't know if A
| happens before B without reading the code. Same issue as
| monad transformers.
|
| Monad transformers don't have this issue. They have a well
| defined order in their types (e.g. `ExceptT e (StateT s)`
| is different from `StateT s (ExceptT e)` the latter blows
| away the state and aborts on an error, while the former
| preserves state on an error).
| isaacimagine wrote:
| When working with monad transformers, I tend to stick to
| constraining functionality over unspecified monad. This
| essentially acts as a set (of constraints), which results
| in all the set-based woes that arise when effects are
| used. So not monad transformers themselves per se, you're
| right.
| contravariant wrote:
| Are you sure you don't mean 'commute'? Monads, being
| endofunctors, compose just fine. Though the composition may or
| may not be a monad I suppose.
| the-alt-one wrote:
| "Monads don't compose" is a common sentence (google it!), for
| example here:
| https://stackoverflow.com/questions/7040844/applicatives-
| com...
|
| Maybe you already knew about this and is still asking,
| though?
| contravariant wrote:
| I wasn't aware. The examples given here are all cases where
| the monad doesn't commute, hence my question.
|
| If two monads commute you _can_ show that the composition
| of the two is (trivially) a new monad, but I 'm not sure if
| the converse also holds.
| kmill wrote:
| It's not very precise talking about whether a monad
| commutes -- it's not clear what "commutes" should really
| mean (certainly not F . G = G . F since that's way too
| restrictive).
|
| I started working it out by hand, but then figured nLab
| had it somewhere, and indeed: https://golem.ph.utexas.edu
| /category/2017/02/distributive_la...
|
| F, G, and F.G are monads if and only if there is a
| "distributive law," which is a natural transformation G.F
| -> F.G satisfying some properties. It's like something
| that satisfies half the braiding properties, where
| braidings are already a weaker version of commutativity.
| isaacimagine wrote:
| As you said, the composition of a monad may not be a monad,
| which means it may not be possible to compose the result of a
| monadic composition with another monad. You can always
| trivially compose a set of effect sets (union), though.
| contravariant wrote:
| That doesn't sound right, even if the composition fails to
| be a monad it's still a functor so there's no reason you
| couldn't keep composing with other functors (in particular
| other monads).
| dllthomas wrote:
| While getting at a real issue, people are being sloppy
| with what they mean by "monads" and "compose" on both
| sides of it.
|
| Types which are monads compose to make types that are
| definitely functors but maybe (probably?) not monads.
| That "maybe not" is what type theory people mean when
| they say "monads don't compose" but it's mostly not
| what's bugging people because...
|
| Most monads can be rewritten as monad transformers (where
| applying them to the identity monad reproduces the
| original monad), which do compose in that way, and this
| is often what people do in Haskell when they have code
| that needs multiple effects.
|
| But! Values of different types (like, say, a `WriterT t
| (Identity u)` and a `WriterT t (IO u)` can't be composed
| using the ever-so-convenient Monad interface which is the
| reason we wanted to make things Monads in the first
| place. This is what the article is discussing and it's
| what programmers often mean when they say "monads don't
| compose".
|
| The workaround in the Haskell ecosystem (at least in
| those parts that haven't adopted effect systems or free
| monads w/ interpreters) is to write as much of your logic
| as possible using an unspecified monad, only asking for
| the functionality you need by way of constraints.
| Constraints compose like gangbusters! Values with
| unifiable types that have different sets of constraints?
| Well, it must've been a type that satisfies all of those
| constraints. And the abstract type is a nice place to
| stick in mocks as you test the pieces of things.
|
| This shares much of the downside of effect sets, in that
| it is awkward to specify order ("layering"?) of effects
| when they do matter; it's possible to be more concrete
| about the types for those pieces but it does spread to
| the pieces that contain them and can sometimes get
| boilerplatey. Whether that's worth it for the added
| safety is going to be context dependent.
| pdpi wrote:
| But that is precisely why we say that functors compose
| (in the general case) and monads don't (again, in the
| general case)!
| dllthomas wrote:
| > Convention (and common sense) dictates the former, but in the
| face of many effects, who's to say there even is an order at
| all?
|
| It _might_ come down to implementation, but I would expect the
| different types to signify different behavior.
|
| With Exn<Promise<T>>, we have something that can throw errors
| in producing an promise, but then that Promise reliably returns
| a T (if it completes at all, but then we probably don't have a
| termination checker in non promise code anyway so we're
| probably used to hand waving that away).
|
| With Promise<Exn<T>>, on the other hand, you definitely have a
| Promise and the computation won't raise an exception until
| after you've awaited it (or equivalent).
|
| You have errors and nondeterminism? Well, do errors cause
| rollback or abort the entire thing?
|
| Nondeterminism and a Writer? Do we collect observations from
| execution traces that didn't complete?
|
| Effect sets either greatly restrict the kinds of effects we can
| talk about (and your example already includes some that we need
| to rule out), or pretend that ordering does not matter when it
| clearly does.
| isaacimagine wrote:
| I agree, Promise<Exn<T>> is an admittedly bad example. See my
| response to the sibling comment for a better example.
|
| Effect sets don't prevent you from using monadic wrappers
| when appropriate. In combination with delimited
| continuations, effects allow languages to reify effectful
| computation in an understandable and computationally
| efficient manner.
|
| > You have errors and nondeterminism? Well, do errors cause
| rollback or abort the entire thing?
|
| It depends on how the handler is implemented, meaning the
| user of a function who is calling an effect has the ability
| to override the way the effect is performed.
|
| Traditional exceptions, exceptions that resume, or
| computations that can be canceled can all be implemented
| using an effect system.
|
| There are a lot of papers on Algebraic effects, but the best
| way to convince you of their utility is to look into the
| literature yourself. The koka language guide, 'Algebraic
| effects for the rest of us' and (later) oleg's work are good
| starting points.
|
| > or pretend that ordering does not matter when it clearly
| does.
|
| Ordering does matter; effects are performed in the order they
| are produced.
| dllthomas wrote:
| > See my response to the sibling comment for a better
| example.
|
| Your better example isn't better; it's a more complicated
| version of an example I discussed.
|
| > It depends on how the handler is implemented, meaning the
| user of a function who is calling an effect has the ability
| to override the way the effect is performed.
|
| Yes, and that's my point. I might write foo with an
| assumption that errors rollback, and bar with an assumption
| that errors abort. Now I can't use foo and bar with the
| same handler, the types don't make that visible, and the
| errors might be subtle. _These things have color_ we 're
| just choosing to be blind to it!
|
| > Ordering does matter; effects are performed in the order
| they are produced.
|
| There are two different senses of "ordering" here - let's
| call mine "layering" then?
|
| > There are a lot of papers on Algebraic effects, but the
| best way to convince you of their utility is to look into
| the literature yourself.
|
| I've read papers, I've been variously excited about them at
| various points over the past ~10 years, but I've come to
| believe they're simply not an improvement over mtl-style
| constraints with layering expressed when we need it.
|
| I believe what's missing from mtl-style is a better way to
| partially specify necessary layering relationships, and
| that's not something I've seen effects systems provide.
| isaacimagine wrote:
| I apologize if my response sounded dismissive, I typed it
| out on my phone in a rush.
|
| > I might write foo with an assumption that errors
| rollback, and bar with an assumption that errors abort.
| [...] These things have color we're just choosing to be
| blind to it!
|
| This is more of a question of API design than something
| inherit to effect systems. I think it would be a good
| idea to use separate types for rolled-back errors and
| aborting errors; in the case where two functions use the
| same effect with different handlers, you can always mask
| the effect[0].
|
| I do not think effect systems ignore color, rather, they
| give the end-user control over how color is manifested.
| Sometimes it's desirable to be blind to color.
|
| On the other hand, I do agree that effect systems throw
| away layering information, much in the same way that
| auto-collapsing a nested Option None to None defeats the
| whole purpose of Option types.
|
| Effects can be used with traditional monads, and it's
| possible to reify effects into monad-like objects that
| can be handled like values. I mean, that's what an effect
| handler does: it matches on the enum of effects in scope
| as if they were a value!
|
| [0]: Scroll down to 3.4.7 in https://koka-
| lang.github.io/koka/doc/book.html#sec-combine
|
| > but I've come to believe they're simply not an
| improvement over mtl-style constraints with layering
| expressed when we need it.
|
| Typed tagless form is great, and (on an unrelated note) I
| love typed tagless final interpreters.
|
| Although I agree that specifying layering relationships
| is an important component of managing effectful
| composition, I think the largest issue with mtl-style
| constraints is that the constraints require higher-kinded
| types by design. HKTs are really cool, but like a lot of
| meta-functiinal features can really drag down compilation
| times.
|
| Effects are nice because they can be implemented as a
| single pass + cps transform, and existing control flow
| constructs can be re-written as sugar in terms of effects
| with no additional overhead (as delimited continuations
| do not require making a copy of the stack).
|
| But I digress. Monads (and more generally HKTs) give you
| powerful tools that allow you to prove certain properties
| about the way effect constraints propogate through a
| system. I advocate for effects not because they are a
| replacement for monads, but because they are a nice
| method for reifying disparate threads of computation in a
| trivially composable manner.
|
| I also don't think that all layering should be replaced
| with effects. I think careful layering with the
| _occasional_ effect when needed is the way to go. Balance
| and simplicity in all things.
| steinuil wrote:
| I was trying to write a comment making the same point but
| honestly, I'm not familiar enough with effects to know what
| exactly you can do with them. Good to know that I wasn't far
| off!
|
| I'm curious about one thing though: unless you're passing a
| parameter that introduces an additional effect F into the
| function, what's the point of making it generic over the effect
| set? In a language with typed effects, all effects that could
| be performed by a function would already be known, so letting a
| unit function `fn get_user<F>() ->{F} T` where i.e. `F:
| DiagnosticData -> ()` decide which instance of an effect that
| takes a DiagnosticData and yields unit, as opposed to `fn
| get_user<F>(g: () ->{F} ()) ->{F} User` where the effect is
| clearly introduced by `g`, seems a bit overkill to me.
| isaacimagine wrote:
| > unless you're passing a parameter that introduces an
| additional effect F into the function
|
| This definitely is overkill, I just wanted to illustrate that
| it was possible. You're correct: usually, a generic effect
| indicates you're passing a closure to the function that may
| execute additional effects: fn
| filter_users<F>( cond: fn<F>(User) ->{F} Bool,
| users: List<User> ) ->{F} List<User>
|
| If filter_users calls the closure cond, effect F will be
| performed, so filter_users must perform F as well.
|
| In the case where there are no external effects, F will be
| inferred to be the union of the empty set and whatever
| effects are used in the body of the function. Some languages
| are actually pretty strict about this, and explicitly
| declaring an empty effect set may mark the function as pure,
| which may not be what you want!
| rawling wrote:
| > Consider a function that is async and throws an error. Is
| this [x] or [y]?
|
| Is it not that one of these is "an async function that may
| throw an error immediately" and one "an async function that may
| throw an error asynchronously"?
| isaacimagine wrote:
| This wasn't the best example, as async is directly tied to
| execution order. But consider a function that is
| nondeterministic, does IO, and may raise an exception. Is
| this a: Ndet<Io<Exn<T>>>
| Ndet<Exn<Io<T>>> Io<Ndet<Exn<T>>>
| Io<Exn<Ndet<T>>> Exn<Ndet<Io<T>>>
| Exn<Io<Ndet<T>>>
|
| There isn't really a good general answer. Most languages
| sweep this under the rug in sone way or other: 'Io, and Ndet,
| and Exn can be done at any point' or 'just use hidden mutable
| state' are common.
|
| By using effect sets, we can extract fine-grained over the
| effects in a program at a given point, without having to
| worry how effects are assembled and dispatched (indeed, in
| effect-based languages, most of these effects can be
| statically inferred).
|
| Most functional languages do model these effects using
| monads, and provide powerful transformations to ensure that
| the ordering of encapsulation doesn't matter. The composition
| is explicit, but at least it's a lot easier than languages
| that don't support monads.
|
| Writing a language is as much about choosing limitations as
| it is features. Going from the untyped to the typed lambda
| calculus limits the number of programs that can be expressed
| (at least until dependent types get involved), but makes it
| easier to reason about the remaining set of programs. Monads
| are more powerful, but harder to reason about. A language
| without monads that doesn't compromise on an effect system is
| a pretty great language in my book.
| rawling wrote:
| Fair enough, thanks for explaining.
| lngnmn2 wrote:
| Enough already.
|
| Monad is a generalization (as a type-class, which is the
| particular way of defining an ADT) of an type-level abstraction
| barrier.
|
| It comes from untyped math which has "structural" notions, such
| as Monoid, and, indeed Monad could be viewed as a Monoid.
|
| There is nothing more "deep" or "profound" in there, and the
| whole mass hysteria is similar to those when Java came out.
| johnday wrote:
| I was half expecting by the end that the punchline of WCIYF was
| going to be either IO (the Haskell data type) or Java's checked
| exceptions, the latter of which is mentioned in WCIYM.
|
| Before we get to the end of WCIYF, we could freely substitute in
| async (promises); the possibility of exceptions (Java-style
| checked exceptions in particular); outside-world interactions
| (Haskell IO); or anything else.
|
| Of course, what we experience in Haskell and other strongly-typed
| functional languages is that we colour our functions all the
| time! Not just red and blue; but sometimes green, or yellow, or a
| combination of several colours at once!
|
| Why is it so ergonomic in a language designed around this
| concept? What we see in JavaScript is that having the colours
| (effectively, the monadic wrappers) isn't enough. We also need
| tools for abstracting and generalising these notions, so that
| composition goes from being a syntactic disaster to a semantic
| convenience.
| dllthomas wrote:
| > We also need tools for abstracting and generalising these
| notions, so that composition goes from being a syntactic
| disaster to a semantic convenience.
|
| I've been speculating for a while that the problem with Java's
| checked exceptions was principally the impoverished language we
| had to talk about them. If we could write things like "this
| throws anything the argument throws, except XYZ" things...
| might've turned out considerably differently.
| cube2222 wrote:
| > you should just bite the bullet and make everything red
|
| I agree! It's also what I've been seeing in i.e. Scala, where
| everything is converging to a single Monad. And in that case,
| monads are basically unnecessary. The point of them is to be able
| to customize your function colors.
|
| Just use a language whose semicolon/newline semantics reflect the
| functionality you'd otherwise get with your "red" monad stack.
|
| For me that means Go, where everything is async by default.
| jayd16 wrote:
| I don't think its so easy. Go goes a bit too far and makes the
| scheduling pre-emptive which can make targeting a specific
| thread (like a UI thread) harder.
|
| I think an ideal language would need to support cooperative
| threading and binding to specific thread contexts.
| metaltyphoon wrote:
| Don't know much but i thought Kotlin did this.
| jayd16 wrote:
| Kotlin does seem to do it well although Kotlin functions
| are still considered colored.
| the_af wrote:
| To me "What Color is Your Function" (the original article this
| one refers to) was so _obviously_ discussing monads that I was
| surprised by the reveal it was specifically focused on async. In
| retrospect I shouldn 't have, because that article was about
| mainstream languages and few of them mention monads by name.
| zaphar wrote:
| The curse of those who have used and understood Monads is that
| we see them everywhere. Async, Exceptions, IO, State machines.
| Everywhere you look you see a monad.
| jillesvangurp wrote:
| Seeing beyond the hammer nail thing is kind of part of
| growing up as an engineer. I had to figure out monads in the
| mid nineties as part of my functional programming course
| work. Lot of fun, not very practical at the time since all
| the mainstream languages of the day were basically not
| functional programming languages. The way people explain
| monads has not improved a lot since there. It still sounds
| like a bunch of gobblygook.
|
| I usually translate in my head to side-effect thingies. Much
| easier. So, Maybe<T> is short hand for it could be there, or
| not, you'd have to check. Same with Result<T>. Or
| Optional<T>. Or Future<T>. It's nicer than having null as a
| default value and then triggering a null pointer exception
| because you forgot to check but a bit less readable if you
| start wrapping them.
|
| But if you have a language with nullable types, like Kotlin,
| you can your cake and eat it. For example, you can have
| functions like isNullOrBlank defined on nullable strings
| (String?) which don't throw a null pointer exception if you
| call them on a null value. Just syntactic sugar for the
| common use case.
|
| Likewise, Kotlin has more syntactic sugar that functional
| programming types would frown upon that is actually nice to
| use. Like suspend functions that you can use to write
| asynchronous programs that almost look like they are normal
| synchronous ones. With proper support for cancelled co-
| routines, error handling, threading, reactive programming,
| etc. Yes, they're colored functions. But, colored functions
| are nice and easy to work with. My compiler will tell me off
| when I do it wrong.
|
| Is it all monads under the hood or just a pragmatic language?
| dllthomas wrote:
| I'm not sure you quite said this but nullable isn't just
| another word for Optional with syntactic sugar. You can
| have Optional<Optional<Integer>>, but Integer?? is not a
| thing. This means you can't (or shouldn't?) use it for,
| say, return value of a generic "get a value from a map"
| because you might want to be able to store nullable values
| in the map and probably want to distinguish "it wasn't
| there" from "it was there but the value was null". IIRC, in
| some languages with generics and nullable you can't
| actually write a function that applies ? to a generic
| argument, because there's no way to say that the generic
| argument isn't itself nullable.
| kelseyfrog wrote:
| I'm not disagreeing, but this is a very good jumping off
| point for what is an enormous discussion in the philosophy
| of programming right now. You've identified the
| correspondence between the programming language support and
| relevant monadic constructions. There's also the principle
| that programming is about applying relevant abstractions.
| The point of contention revolves around the question of
| "Should monads be available to programmers in the abstract
| where programmers are supported enough to write their own
| monads? Or should monads be reified into language
| constructs, thus making it a up to the language designer to
| choose the finite set of available monads and syntactify
| them into language constructs?
|
| A vocal camp wants monads in the abstract and feels like
| reified monads as language constructs completely misses the
| point. The other camp is happy to accept new syntax and
| keywords to get their work done and see monads as ivory
| tower language enthusiasm. The choices that language
| designers make increasingly put their languages on one side
| or another of this dividing line.
| duped wrote:
| This is true of any sufficiently general abstraction. Like a
| multi graphs and effects systems.
|
| In fact overly general abstractions are an anti pattern in
| practical software development, at least in my opinion. Any
| problem can be successfully extrapolated to a more general
| case, that doesn't mean it's worth doing it.
| zaphar wrote:
| They do give you a template for approaching certain
| problems though. Even if your implementation of the
| template in your head is specific and therefore easier to
| follow in the implementation. It's useful to have an
| understanding of them for that reason.
| saurik wrote:
| I mean, the "curse" part is only being forced to live amongst
| people naive to this reality: seeing monads for what they are
| _should be_ a blessing.
| jpitz wrote:
| You can choose to see this as a curse or an opportunity, a
| la https://xkcd.com/1053/
___________________________________________________________________
(page generated 2022-02-16 23:02 UTC)