3 ms·
I don't want to say what I'm about to say, but... I don't care about the type signature of promises. I actually don't care about the type of anything which has
by slashnull 12y ago
I don't want to say what I'm about to say, but...
I don't care about the type signature of promises. I actually don't care about the type of anything which has a complicated type; I think it's just incredibly winning that I will now be able to describe the shape of the raw data circulating in my code.
I've always found really annoying, in Haskell, to see incredibly complex type idioms emerging to allow stuff that doesn't really deserve it. And yes, I'm talking about monad transformers.
Don't get me wrong, I think that Haskell and the typing techniques and idioms it has fostered are a tremendous achievement, but right now, I'm more focused on bringing my web code, which right now is unfortunately a jungle of implicitly-typed garbage, closer to a safe and predictable better-typed form.
I tried to do that in Haskell in the back-end, but everytime I tried, I lost mind-boggling amounts of time dealing with the monadic stack of the framework I tried.
As the saying goes, I'm not clever enough to use dynamic typing, and bugs happen. Unfortunately, I'm also not clever enough to use real strong typing, and nothing compiles, let alone gets done.
Hence why I'm immensely thankful to see facebook embracing gradual typing in a way that lets me leverage my knowledge of algebraic typing.
- inglor 12y agoYou make some good points here, I've also experienced the same with Haskell web frameworks. I think we can agree that the most important things to annotate are points of interaction. If I'm part of a 5 developer team working on a code base, or I'm using a library someone made - I want the functions I'm calling to be very explicit about what they take and return and I want the functions I provide others to be very clear on what they take and return. The problem is that even something people use every day like a Promise or an event handler creates very complex types (like the example above). I think any viable solution that expects to be type safe needs to be able to express that. If your code does not expose any callbacks, or anything async I'd say it's simply not very typical JS code. The alternative of course is to be _less_ safe about our type. We could say that we treat a promise as: Promise<A> -> A -> Promise<B> -> Promise<B> (just a `bind` from Haskell) - this would let us maintain _some_ type safety which is better than nothing.
- slashnull 12y agoI agree that the facilities to express function composition are woefully insufficient in nearly all languages besides ML derivatives, and that nearly all of the recent frameworks and techniques to express concurrency rely heavily on function composition.
- tel 12y agoI'd like to argue from a Haskell backend perspective that monad transformers deserve exactly the complexity they expose. They give you a handle to factor side effects in sensible ways and to express that code requires exactly some set of effects and no others. It is not always clear how to factor code which does not have this rigor into effect-typed form. It can be extraordinarily difficult to recognize what effects are being carried out in which parts of code at first—especially if you haven't been forced into the discipline early. Thus, I find it unsurprising that you feel the translation effort is challenging. But, coming from the other angle—building things with well-defined effect typing from day zero and composing pieces atop one another to reach your final complexity goal—works exceedingly well. Better, it forms a design which can be translated to untyped settings and retain its nice composition properties. Which is to say not much more than: there is some logic to all that madness and once you're on the "other side" it's hard to judge these "incredibly complex type idioms" as anything other than useful and nearly necessary for sane code reasoning. I miss transformer stacks a lot when using other languages.
- slashnull 12y ago... said the monk, sitting cross-legged with an air of zen inner peace ; ) I think it all boils down to considerations of idealism vs. realism, in the end, and yes, I have to admit, I managed to get a sort of big picture understanding of Yesod and its ORM, and it's definitely a cool design. Can't say I know as much about Happstack, but the hello worlds were smaller. And the authors didn't have to invent two dependency management tools to get it to compile... /off-topic I actually used something akin to monads to write a quick and dirty parser combinator library... In PHP! It was really fun, but I have to admit that it got really hard to keep track of what was function, what was return, what was supposed to be passed along to the next function and so on without any real typing. For the first time I... I wanted monads. But still, I have 99 problems and I'd say 97 of them are undefined, nulls, erroneous typecasts, unexpected layers of wrapping, and so on. I wrote bad code, my teammates did, and here we are. I want to get rid of those so that I can, at last, have real problems. Then I'll port my REST server in Haskell ; )
- tel 12y ago
- lmm 12y agoThere's certainly a hierarchy; if you don't have typed data then you won't benefit from generics. If you don't have typed containers then forget about monads. If you don't have simple monads then no point worrying about how to compose them. And yet, as someone who's been working in Scala for nearly 5 years now, more and more of these abstractions are starting to seem "worth it". My first Scala code was quite imperative, mixing random effects left and right. But eventually I started handling async calls explicitly - or perhaps I should say, I became fluent enough in the language that I could make an explicit distinction between sync and async calls without in being too cumbersome - and then I reaped the rewards, with more reliable, more performant, more maintainable code. And then I did the same thing with error handling, replacing surprise exceptions with an explicit Either (stacking this inside the Futures), and again I found my code became clearer, easier to reason about. I've just finished factoring out database access into a Free monad based construct, and for the first time in my life I can test database access in a smarter way than just creating a (possibly in-memory) testing database and hoping it does the same things a real database would do. The monad tools are good enough to make it easy - as easy as "magic" Spring AOP, but explicit and ordinary. I've written a library for dealing with monad stacks that I'm sure would have horrified myself of three years ago (https://github.com/m50d/scalaz-transfigure https://github.com/m50d/scalaz-transfigure), but I've come here through small incremental steps that have made sense at every stage (it helps that I'm a big believer in Agile). If I'd been dropped in it with a language like Haskell where everything has to be monadic from day 1, I think I'd've given up. I still wouldn't use monads for I/O (at least, not yet) - the advantages don't seem worth the overhead. But I'm glad I'm working in a language where these things are possible, and where I can gradually adopt them in my own time.