Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Beautiful and terrible Haskell [closed]

Haskell is beautiful. That's a fact. It's concise, fast etc. Many of you will admit that writing in it is a great pleasure. On the other hand I think that its conciseness might be a disadvantage when people try to write too sophisticated code. For example I recently opened my friend's project and found something like this:

stationDepartures id sc=map (\(s1,list)->(stationName $ (stationMap $ system sc) Map.! s1,list)) ( Map.toList (Map.fromListWith (++) (map (\((s1,s2),(d,start,dur))->(s2,[start])) (Map.toList (Map.filterWithKey (\(s1,s2) _ ->s1==id) (Map.unions (List.map times (Map.elems $ schedule sc))))))))

This one-liner was absolutely unreadable to me. This is of course a quite extreme example but it helped me to realize that maybe my Haskell code might seem unreadable to others. I started to wonder what are the principles of creating a beautiful code in Haskell. I found that e.g. lambda functions are considered by some people as superfluous because they make the code less readable. What do you think about that? What requirements should Haskell code meet to be considered as "beautiful"?

like image 386
Adam Sznajder Avatar asked Nov 30 '22 05:11

Adam Sznajder


1 Answers

I'm going to assume that you asked, "How can I write this one-liner in a more readable fashion?" Otherwise, the question is a bit too subjective.

One-liners are problematic in any language, although Haskell can be especially problematic due to the abundance of binary operators (with precedence rules), higher order functions (which are difficult to name), and (gasp!) higher-order functions which are binary operators. However, we can start by noticing that most of the parentheses can be eliminated in the one-liner, and we can show that the expression is a series of functions composed together. Each function can be written on a separate line and composed with ., with a final $ to apply the whole thing to an argument.

stationDepartures id sc =
  map (\(s1,list) -> (stationName $ (stationMap $ system sc) Map.! s1,
                      list)) .
  Map.toList .
  Map.fromListWith (++) .
  map (\((s1,s2),(d,start,dur)) -> (s2,[start])) .
  Map.toList .
  Map.filterWithKey (\(s1,s2) _ -> s1 == id) .
  Map.unions .
  List.map times .
  Map.elems $ schedule sc

To me, this is fairly readable because I recognize patterns in there, and I can follow the flow of data from bottom to top. (Note: this is why all of the Map functions take a Map as the last parameter instead of the first parameter. Remember this when you are designing your Haskell APIs!)

For example, the Map.toList . Map.fromListWith f pattern is very common, it's just hard to see it in the one-liner (to my eyes, at least). The code isn't really that sophisticated, it's just had it's line breaks surgically removed.

But, readability is subjective.

You'll develop a style that is easy for you to read. As you understand the language more, the style will evolve, and your sense of "beautiful code" will evolve. Just let it happen, and try not to get bogged down too much in rewrites that you don't have fun any more.

like image 179
Dietrich Epp Avatar answered Dec 05 '22 06:12

Dietrich Epp