7
votes

I'm learning haskell, and I have a lot of difficulty with mentally parsing many haskell expressions I come across.

Of course, I expect that, with enough practice, mentally parsing haskell will become second-nature, but in the meantime, in order to make sense of what I come across, I'd like to find some automatic way to translate an arbitrary "standard haskell"1 expression into one in which all "ambiguous"2 sub-expressions have been eliminated by introducing the necessary parentheses.

For example, it would translate the expression

f g h i

...into

((f g) h) i

..., or

a -> b -> c -> d

...into

a -> (b -> (c -> d))

..., etc.

Preferably, this would be a tool I can access with my phone, since I do much of my reading on haskell away from a proper computer.


1Of course, no such tool could possibly work with custom operators of unknown fixity and associativity. By "standard haskell" I mean the stuff defined in the prelude and in the standard haskell library.

2I'm using "ambiguous" here as shorthand for "ambiguous in the absence of precedence rules". E.g. 2 + 3 * 5 is ambiguous unless there's some precedence rule that settles the question of which of the two operations will be performed first.

2
I can`t think of anything on your phone, but there are some pretty straightforward things you can do in GHCi - Alec
Well, haskell-src-exts makes it easy to get a completely unambiguous parse tree and print it out. But it ain't really readable. Try (fmap . fmap . fmap) (const ()) (parseFile "foo.hs") from ghci once you've installed it. - Daniel Wagner
@Alec: what exactly can I do in ghci? (about the phone: that was just a preference, and a mild one at that, in case there was a choice in the matter; if there isn't, i'll gladly settle for ghci.) - kjo
This brand new tool seems to do exactly what you describe in the term level. - duplode
Thank you for the heads-up! It looks like a godsend for the haskell noob (like me). - kjo

2 Answers

10
votes

If you really want to go to the work of it, you could write a TemplateHaskell function to do this for you - you essentially just walk the AST and add parens at will. I started doing that, but realized it will get pretty long and tedious pretty fast. I think it may be more convenient for the moment for you to only think about currying when it actually comes into play (with functions that are not fully applied).


However, there is a trick you can use for a subcase of your problem: parens around operators whose fixity and associativity you aren't familiar with. In GHCi, after booting up with the right flags, just wrap the expression you are interested in inspecting in $([| <expression> |]). Then, you get to see a parenthesized version of your expression before the result of evaluating it.

$ ghci -ddump-splices -XTemplateHaskell
Prelude> $([| 1 + 2 ^ 3 * 4 |])
<interactive>:1:3-21: Splicing expression
    [| 1 + 2 ^ 3 * 4 |] ======> (1 + ((2 ^ 3) * 4))
33
Prelude> $([| 1 <$ pure 4 >>= \x -> const mempty =<< [(+),(*)] <*> [1,2] <* [False] |])
<interactive>:2:3-77: Splicing expression
    [| 1 <$ pure 4
       >>=
         (\ x_a6PT -> const mempty =<< [(+), (*)] <*> [1, 2] <* [False] |]
  ======>
    ((1 <$ (pure 4))
     >>=
       (\ x_a6PT
          -> ((const mempty) =<< (([(+),(*)] <*> [1,2]) <* [False]))))
[]
Prelude>

However, this definitely won't fix the type signatures or function applications the way you want.

4
votes

It's not exactly what you're asking for, but I've found using HLint to warn me about unnecessary parens when I'm writing Haskell to be a big help when I'm reading.

You may also find the chart on page 23 of Bernie Pope's "A tour of the Haskell Prelude" helpful. I've included a copy below.

page 23 of Bernie Pope's "A tour of the Haskell Prelude"