Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to automatically parenthesize arbitrary haskell expressions?

Tags:

haskell

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.

like image 543
kjo Avatar asked Oct 30 '16 16:10

kjo


2 Answers

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.

like image 166
Alec Avatar answered Jan 03 '23 20:01

Alec


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"

like image 44
Colin Barrett Avatar answered Jan 03 '23 21:01

Colin Barrett