8 ms·
And why restrict oneself to just two colours? Haskell monads also allow one to abstract over the "colour", such that one can write polymorphic code that works f
by willtim 5y ago
And why restrict oneself to just two colours? Haskell monads also allow one to abstract over the "colour", such that one can write polymorphic code that works for any colour, which I think was the main objection from the original red/blue post.
Microsoft's Koka is an example of a language that further empraces effect types and makes them easier to use:
https://koka-lang.github.io/koka/doc/index.html https://koka-lang.github.io/koka/doc/index.html
- the_duke 5y agoThe Unison language [1] also has a very interesting effect system. [1] https://github.com/unisonweb/unison https://github.com/unisonweb/unison
- inglor 5y agoBasically, in JS you could have multiple colors (with generators that imitate `do` notation pretty well) and you can obviously implement any monadic structure with bind you'd want. The thing is: having less generic abstractions (and fewer colors) makes certain things (like debugging tooling and assumptions when reading code) much much nicer.
- fbn79 5y agoJavascript must solve the fundamental problem that generators are (by implementation) not clonable. So generators can not be used generally as do notation.
- benrbray 5y agoFun Fact: The author of Koka (Daan Leijen) wrote a LaTeX-flavored Markdown editor called Madoko [1] using Koka! It is used to render the "Programming Z3" book [2], for instance. [1] http://madoko.org/reference.html http://madoko.org/reference.html [2] https://theory.stanford.edu/~nikolaj/programmingz3.html https://theory.stanford.edu/~nikolaj/programmingz3.html
- zozbot234 5y agoMonads are not the same thing as effect types - the latter are most commonly modeled as Lawvere theories. They are at a different level of composability. Regardless, this blogpost is about Rust which has yet to gain either feature, for well-known reasons (for one thing, monadic bind notation interacts badly w/ the multiple closure types in Rust). Talking about Haskell monads just does not seem all that relevant in this context.
- tome 5y agoCould you elaborate? Firstly, I'm not aware of any programming languages that has Lawvere theories as a first-class concept. Secondly, when I last looked into Lawvere theories I couldn't work out any way of making them into a first-class concept in any way that would make them distinct from monads. (On the other hand Lawvere theories may be useful for modelling effects in type theories. That may be so (I don't know) but is an orthogonal issue.)
- sullyj3 5y agoI don't really know much about this area, but there's a language literally called Lawvere. https://github.com/jameshaydon/lawvere https://github.com/jameshaydon/lawvere
- flqn 5y agoMonads are not the same as effect types, but Haskell affords implementing effects using monads, so I'd argue the comparison is relevant.
- willtim 5y ago> Monads are not the same thing as effect types Monads can be used to implement "effect types" and define the semantics of them. I believe this is how Koka works. > Talking about Haskell monads just does not seem all that relevant in this context. I respectfully disagree. Async/Await first appeared in F# as a Monad (Computational Workflow). Monads also do not necessarily have to involve closures at runtime, they can be useful just to define semantics and build type-checkers.
- deleted 5y ago
- mumblemumble 5y agoI'm not sure it's correct to understand monads as a kind of function coloring mechanism. Two reasons. First, monads aren't functions. Second, the whole function coloring thing wasn't a complaint about limits on function composition. If it were, we might have to raise a complaint that arity is a kind of color. It was a complaint that functions of one color can't be called from inside functions of another color. And you don't really have an equivalent problem with using one monad inside the implementation of another monad.
- valenterry 5y agoYou have the same problem with monads. You cannot have a (pure) function, that calls another function which returns an IO monad, without returning an IO monad itself _or_ ignoring the result of the function call (which is exactly the same as not calling it from the beginning). Hence the semantics are the same - just without any compiler feature like async/await.
- mumblemumble 5y agoThat's a special case for IO, isn't it? I guess I've never actually tried this, but I don't think it's generally the case for all monads. IOW, Haskell does have coloring, and its coloring does manifest through the IO monad, but that does not mean that all monads have color. Sort of like how you can't do, "Socrates is a man, Socrates is dead, therefore all men are dead."
- valenterry 5y agoI think I see what you mean. However, it is the other way around: some monads are special cases in the way that they have extra methods that allow you to "escape" their context. Like lists: you can fold over them and get out a different result type. But in general you can't do that with monads, in particular not if you just have "any" monad at hand but you don't know which one it is. And IO is one example, parsers would be another one.
- 5y ago
- simiones 5y agoDon't monads in themselves have the same problem of introducing "coloring" (where each monad is a different color) , so that functions which work for one monad won't work with easily compose with functions which work with another monad? For example, isn't it true that you can't easily compose a function f :: a -> IO b with a function g :: b -> [c] (assuming you don't use unsafePerformIO, of course)? Of course, there will be ways to do it (just as you can wrap a sync function in an async function, or .Wait() on an async function result to get a sync function), and Haskell's very high level of abstraction will make that wrapping easier than in a language like C#.
- klodolph 5y agoYou can, h :: a -> IO [c] h = liftM g . f Or fmap, or use various arrow operators, or <$>. I might describe it as “pathologically easy” to compose functions in Haskell, since it’s so much easier in Haskell than other languages (and mixing monads & pure code is still very easy).
- linkdd 5y ago> I might describe it as “pathologically easy” to compose functions in Haskell Maybe because "composing functions" is the whole point of category theory, monads and Haskell?
- klodolph 5y agoThe point of category theory is composing morphisms! Functions are morphisms in the Set category. Haskell has something which looks very similar to category theory, but the functions and types in Haskell do not form a category. I would call it “category theory inspired”.
- linkdd 5y agoThank you for the clarification. But still, since it's all about composition, the simplicity is a direct consequence IMHO.
- pron 5y agoThe problem is that it is unclear that effect systems are something worth having in the first place. A type system could introduce many kinds of distinctions -- the unit allocates memory or not, the unit performs in a given worst-case time or not, etc. etc. -- but not everything it could do it also should. Every such syntactic distinction carries a cost with it (it tends to be viral), and for each specific distinction, we should first establish that the benefits gained outweigh those costs. Otherwise, it's just another property among many that a programming language may have, whose value is questionable.
- BenoitP 5y ago> the unit allocates memory or not, the unit performs in a given worst-case time or not, etc. etc. -- but not everything it could do it also should Strong agree. This list could also be extended endlessly, and with continuous dimensions: how much L1i/L2i/L1d/L2d/L3 cache does this consume? How many instructions will it generate? For AMD64, ARM? How well can it be reordered wrt this other function? Is it superscalar? It becomes dimensionally intractable, because these properties do interact with each other. IMHO, all side-effects considerations should be (easily) surfaced as hints to the programmer, but not as constraints. At least that's what a language/runtime should aim for. For example: Rust's asyncs can be called synchronously. That's fair enough (and ok), but the programmer should be made aware by the compiler that it will block the thread. To me, the best thing to aim for is a language that gets out of the way (correctness in changing requirements context is difficult enough already), but informs you on what you are trading off. Say your wrote a sequential part. A hypothetical 'parallelization opportunities' tab of your IDE should ask you the question: 'Do your business requirements allow that this part be made parallel?'.
- pron 5y agoEven if you could do it in a non-obtrusive way, I would argue that this is a very wrong direction for many languages. The reason is that the static information is only the worst-case. How much a cache a subroutine consumes is a dynamic property, as is how long it runs or whether or not it blocks a thread. This worst-case information, reflected statically at the language level, is useful only when you're aiming to optimise for the worst-case, as you do in hard realtime domains; in most other domains, you might want to optimise amortised or even average costs. The reason this is being considered for removal is that even though the mechanism is very flexible and powerful in theory, it is too elaborate to be used correctly by most programmers in practice.