2 ms·
Thanks for writing this explanation. Now I’ll explain the case against it: I think I’d want write out the function for a transformation from a list of futures
by skybrian 3y ago
Thanks for writing this explanation. Now I’ll explain the case against it:
I think I’d want write out the function for a transformation from a list of futures to a future containing a list, give it a name, and explain what it does. This is waiting for all the futures to settle, right? And you have to explain what happens when any of them fails.
And indeed, this is a standard library function [1].
Deriving it instead of naming and explaining it will make it harder to understand any code using the derivation because you can’t look up what Promise.all does. If the code and explanation is written in terms of Arbitrary instead of Future then the explanation will be difficult to write and difficult to read. Defining it for an arbitrary datatype instead of a List would complicate the explanation too.
The lesson of monad tutorials is that there are some levels of abstraction that are usually not worth it because they’re too hard to explain. Explaining how async works is difficult enough.
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- tsss 3y agoAnd how are you going to write all the functions for the countless different combinations of types? This is what many users of the mtl library do and it's clear that it doesn't scale, it's error prone and the code is often not even much more readable. Clearly, some kind of mechanism that is easily composable without ceremony is needed. The real problem is that lifting/unlifting monad transformers which are not monad morphisms (mostly those that have some kind of state) is inherently unsafe. This is where MonadTransControl and MonadBaseControl classes come into play and the whole controversy surrounding them. Haskellers have debated for years about the correct semantics to use here.
- skybrian 3y agoI don't need to write all those functions that I'll never call. I write the few that I use in my own code, which is probably doing something fun or practical rather than abstract. (But if you use these abstractions extensively, maybe Haskell is for you?)
- sfvisser 3y ago> Deriving it instead of naming and explaining it will make it harder to understand any code using the derivation because you can’t look up what Promise.all does. If the code and explanation is written in terms of Arbitrary instead of Future then the explanation will be difficult to write and difficult to read. To the contrary! Because I’ve already used that function (traverse) a million times and know exactly how to write it myself I understand exactly what it does without any knowledge about the details of your Future or Optional or whatever. True and meaningful abstraction. Which is truly an amazing super power!
- skybrian 3y agoThat's okay if you're just writing for yourself. But the next person reading your code might never have used traverse and won't get it. Then this becomes a question of what sort of common language we should expect of programmers. That's always going to depend on context. Here are some people trying and mostly failing to explain what 'traverse' does: https://stackoverflow.com/questions/7460809/can-someone-explain-the-traverse-function-in-haskell https://stackoverflow.com/questions/7460809/can-someone-expl...
- themk 3y agoIf you use a language which has monads, then these things quickly become second nature. traverse, sequenceA, etc are all sorely missed when I use rust, as they are quite fundamental in languages which allow the abstraction. It honestly provides so much for free when using a new library.