RFC: Implicit parentheses in GHCi
I've been making a few tickets lately that aren't technical but small things that would streamline my interactive experience. Please close if this is not the place for it. Caveat lector: long and boring read ahead.
Code
data Op = Add | Mul deriving Show
data Exp = Op Op | Lit Int | Exp :$ Exp deriving Show
infixl :$
Motivation
I often want to see the “progression” of applying an operator to arguments one by one. There are several ways of doing this:
>>> :t (:$)
>>> :t (Op Add :$)
>>> :t Op Add :$ Lit 10
>>> :t (:$)
>>> :t (Op Add :$)
>>> :t (Op Add :$) (Lit 10)
>>> :t (:$)
>>> :t (:$) (Op Add)
>>> :t (:$) (Op Add) (Lit 10)
>>> :t (:$)
>>> :t (:$) (Op Add)
>>> :t Op Add :$ Lit 10
Look at all of those parentheses! Every step adds or removes (or requires navigation around) parentheses.
tl;dr
: this is annoying to type
I'm writing this out in full not just because I get a sick joy out of it but to hammer the point home.
First example
We wrap :$
in parentheses, navigate around them to make a section and then remove them:
>>>
>>> :t (:$)␣
-- C-m
>>>
-- C-p
>>> :t (:$)␣
-- C-a
>>> ␣:t (:$)
-- C-f, C-f, C-f, C-f
>>> :t (␣:$)
>>> :t (Op Add ␣:$)
-- C-m
>>>
-- C-p
>>> :t (Op Add :$)␣
-- C-h
>>> :t (Op Add :$␣
>>> :t (Op Add :$ Lit 10
-- C-a
>>> ␣:t (Op Add :$ Lit 10
-- C-f, C-f, C-f
>>> :t ␣(Op Add :$ Lit 10
-- C-d
>>> :t ␣Op Add :$ Lit 10
Second example
We wrap :$
in parentheses, navigate around them to make a section and then wrap the argument in parentheses:
>>>
>>> :t (:$)␣
-- C-m
>>>
-- C-p
>>> :t (:$)␣
-- C-a
>>> ␣:t (:$)
-- C-f, C-f, C-f, C-f
>>> :t (␣:$)
>>> :t (Op Add ␣:$)
-- C-m
>>>
-- C-p
>>> :t (Op Add :$)␣
>>> :t (Op Add :$) (Lit 10)␣
Third example
Wrap everything in parentheses, on a good day I will remember that the arguments need parentheses:
>>>
>>> :t (:$)␣
-- C-m
>>>
-- C-p
>>> :t (:$)␣
>>> :t (:$) (Op Add)␣
-- C-m
>>>
-- C-p
>>> :t (:$) (Op Add)␣
>>> :t (:$) (Op Add) (Lit 10)␣
((On every other day I will forget that Op Add
is an application and write..))
>>> :t (:$)␣
>>> :t (:$) Add␣
-- M-b
>>> :t (:$) ␣Add
>>> :t (:$) (Op ␣Add
-- C-e
>>> :t (:$) (Op Add␣
>>> :t (:$) (Op Add)␣
Fourth example
Wrap operator and argument, remove both pairs of parentheses:
>>>
>>> :t (:$)␣
-- C-m
>>>
-- C-p
>>> :t (:$)␣
>>> :t (:$) (Op Add)␣
-- C-m
>>>
-- C-p
>>> :t (:$) (Op Add)␣
-- C-a
>>> ␣:t (:$) (Op Add)
-- C-f, C-f, C-f
>>> :t ␣(:$) (Op Add)
-- C-d, C-d, ...
>>> :t ␣Op Add)
-- C-e
>>> :t Op Add)␣
-- C-h
>>> :t Op Add␣
>>> :t Op Add :$ Lit 10␣
Proposal
Long story short this involves a lot of manipulation of terms (deleting/rewriting, cutting/pasting), jumping around and adding or removing parentheses.
My proposal is to make GHCi a bit smarter in how it accepts operators and sections, to a first approximation it would be as if there were implicit '(' ++ cmd ++ ')'
around the command. Let's see how I picture this would work:
>>> :t :$
>>> :t Op Add :$
>>> :t Op Add :$ Lit 10
This may feel disconcerting since this is not valid syntax for operators or sections, but I think it would be fine. For example both :info :$
and :info (:$)
work. This would take me a lot less time to write! No parentheses and minimal jumping around.
This would make it very simple to get the right section of an operator:
>>> :t >>=
>>> :t >>= id
and could be extended to work with infix functions as well, I would probably use flip
a whole lot less:
>>> :t `take`
-- ^ probably shouldn't work, but allows for less skipping
>>> :t `take` "wabalabadubdub"
>>> :t 10 `take` "wabalabadubdub"
The reason why I said “to a first approximation” is that :$) (Op Add
should obviously not be valid.
Trac metadata
Trac field | Value |
---|---|
Version | 8.0.1 |
Type | FeatureRequest |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | GHCi |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |