4 ms·
I think it is a cultural mismatch. The Haskell community seem to be very influenced by mathematicians and their way of describing everything as abstractly a pos
by olavk 12y ago
I think it is a cultural mismatch. The Haskell community seem to be very influenced by mathematicians and their way of describing everything as abstractly a possible. You can even see it in code examples where it is pretty common to see arbitrary one-letter variable names.
The software development community is by and large much more pragmatic and focused on solving real-world problems. They have an easier time understanding concepts and code examples when explained in the context of real-world problems.
So when the Haskell people try to explain monads, they start with the abstract definition, the monadic laws and so on, and they focus on explaining the concept of a monad, rather than what they are practially useful for. Even the many infamous monad-metaphors (its like a spacesuit! No its like a burrito!) try to find a metaphor for the abstract concept of a monad, not for any practical use. For the typical developer this might seem like a lot of mathematical wanking for little benefit.
The language F# seem to have a more 'pragmatic' culture. 'Monads' are called 'computation builders' in F#, and the equivalent to do-notation is called 'computation expression'. Yeah it is just naming, but it shows a focus on the use of monads rather than on the underlying mathematical concept.
A typical F# example of the use of monads - sorry, computation builders is the Async monad:
let AsyncHttp(url:string) =
async {
let req = WebRequest.Create(url)
let! rsp = req.GetResponseAsync()
use stream = rsp.GetResponseStream()
use reader = new System.IO.StreamReader(stream)
return reader.ReadToEnd()
}
This example fetches a resource over http without blocking. It is immediately obvious why this is useful. After all, similar functionality was recently added to C# through the keywords 'async' and 'await'. But in F# it didn't require the addition of new keywords but could be implemented as a library, due to support for monads in the language.
- chriswarbo 12y ago> You can even see it in code examples where it is pretty common to see arbitrary one-letter variable names. This is usually because Haskell functions are really short. For example: compose f g x = f (g x) We could write this with more descriptive variable names, but there are only 3 variables and they're only in scope for one very tiny line: compose outerFunction innerFunction argumentForInnerFunction = outerFunction (innerFunction argumentForInnerFunction) There are certain conventions as well; if a variable must be present, but isn't used, it gets called `_`: first (x, _) = x Lists tend to get a plural "s" on their name: elem x xs = any (map (x ==) xs) > So when the Haskell people try to explain monads, they start with the abstract definition, the monadic laws and so on, and they focus on explaining the concept of a monad, rather than what they are practially useful for. For the typical developer this might seem like a lot of mathematical wanking for little benefit. This is because functional programmers tends to spend the most time on writing generic, abstract, re-usable libraries. Particular applications are just thin veneers on top of these libraries. For example, I might spend most of my time writing generic, abstract code to deal with tuples. To make an application, I can just pick and choose the library code I want to use: type Name = String type Address = String type Quantity = Int type OrderID = Int type SKU = Int type Customer = Tuple Name Address type Order = [Tuple SKU Quantity] type Invoice = Tuple Customer Order Given that I'm using Tuples for Orders, Invoices and Customers, the time I spent writing abstract code to deal with tuples wasn't "a lot of mathematical wanking for little benefit". The time spent writing generic, abstract monad libraries isn't wasted either; by picking and choosing which monads to use, our application can automatically get error-handing, non-determinism, backtracking, exceptions, state, software transactional memory, etc.
- dllthomas 12y agoMoreover, there are some conventions (which you followed but didn't call out) that make the single letter names clearer. Whether that makes them clear enough is more debatable, but I think they're worth noting. For values: f, g, h: functions x, y, z: numbers or generic values m, n, o: monadic values For polymorphic types: a, b, c: arbitrary value type s, t, u: state or structure f, g, h: functor (possibly applicative) m, n, o: monad i: index There are probably some others that I'm not thinking of, too.
- Jweb_Guru 12y agoWhile computation expressions indeed appear to capture the concept of do notation, as far as I know, F# lacks the machinery (higher kinded polymorphism) to be able to express the abstract monadic concept (though it can obviously express specific monads, just like most modern languages). So I'm not sure it's a very good example of how much more "pragmatic" F# culture is so much as it is an example of a feature F# doesn't actually have.