• Matthías Páll Gissurarson's avatar
    Add flag to show docs of valid hole fits · c4d98341
    Matthías Páll Gissurarson authored
    One issue with valid hole fits is that the function names can often be
    opaque for the uninitiated, such as `($)`. This diff adds a new flag,
    `-fshow-docs-of-hole-fits` that adds the documentation of the identifier
    in question to the message, using the same mechanism as the `:doc`
    command.
    
    As an example, with this flag enabled, the valid hole fits for `_ ::
    [Int] -> Int` will include:
    
    ```
    Valid hole fits include
      head :: forall a. [a] -> a
        {-^ Extract the first element of a list, which must be non-empty.-}
        with head @Int
        (imported from ‘Prelude’ (and originally defined in ‘GHC.List’))
    ```
    
    And one of the refinement hole fits, `($) _`, will read:
    
    ```
    Valid refinement hole fits include
      ...
      ($) (_ :: [Int] -> Int)
          where ($) :: forall a b. (a -> b) -> a -> b
          {-^ Application operator.  This operator is redundant, since ordinary
              application @(f x)@ means the same as @(f '$' x)@. However, '$' has
              low, right-associative binding precedence, so it sometimes allows
              parentheses to be omitted; for example:
    
              > f $ g $ h x  =  f (g (h x))
    
              It is also useful in higher-order situations, such as @'map' ('$' 0) xs@,
              or @'Data.List.zipWith' ('$') fs xs@.
    
              Note that @($)@ is levity-polymorphic in its result type, so that
                  foo $ True    where  foo :: Bool -> Int#
              is well-typed-}
          with ($) @'GHC.Types.LiftedRep @[Int] @Int
          (imported from ‘Prelude’ (and originally defined in ‘GHC.Base’))
    
    ```
    
    Another example of where documentation can come in very handy, is when
    working with the `lens` library.
    
    When you compile
    ```
    {-# OPTIONS_GHC -fno-show-provenance-of-hole-fits -fshow-docs-of-hole-fits #-}
    module LensDemo where
    
    import Control.Lens
    import Control.Monad.State
    
    newtype Test = Test { _value :: Int } deriving (Show)
    
    value :: Lens' Test Int
    value f (Test i) = Test <$> f i
    
    updTest :: Test -> Test
    updTest t = t &~ do
        _ value (1 :: Int)
    ```
    
    You get:
    ```
      Valid hole fits include
        (#=) :: forall s (m :: * -> *) a b.
                MonadState s m =>
                ALens s s a b -> b -> m ()
          {-^ A version of ('Control.Lens.Setter..=') that works on 'ALens'.-}
          with (#=) @Test @(StateT Test Identity) @Int @Int
        (<#=) :: forall s (m :: * -> *) a b.
                 MonadState s m =>
                 ALens s s a b -> b -> m b
          {-^ A version of ('Control.Lens.Setter.<.=') that works on 'ALens'.-}
          with (<#=) @Test @(StateT Test Identity) @Int @Int
        (<*=) :: forall s (m :: * -> *) a.
                 (MonadState s m, Num a) =>
                 LensLike' ((,) a) s a -> a -> m a
          {-^ Multiply the target of a numerically valued 'Lens' into your 'Monad''s
              state and return the result.
    
              When you do not need the result of the multiplication,
              ('Control.Lens.Setter.*=') is more flexible.
    
              @
              ('<*=') :: ('MonadState' s m, 'Num' a) => 'Lens'' s a -> a -> m a
              ('<*=') :: ('MonadState' s m, 'Num' a) => 'Control.Lens.Iso.Iso'' s a -> a -> m a
              @-}
          with (<*=) @Test @(StateT Test Identity) @Int
        (<+=) :: forall s (m :: * -> *) a.
                 (MonadState s m, Num a) =>
                 LensLike' ((,) a) s a -> a -> m a
          {-^ Add to the target of a numerically valued 'Lens' into your 'Monad''s state
              and return the result.
    
              When you do not need the result of the addition,
              ('Control.Lens.Setter.+=') is more flexible.
    
              @
              ('<+=') :: ('MonadState' s m, 'Num' a) => 'Lens'' s a -> a -> m a
              ('<+=') :: ('MonadState' s m, 'Num' a) => 'Control.Lens.Iso.Iso'' s a -> a -> m a
              @-}
          with (<+=) @Test @(StateT Test Identity) @Int
        (<-=) :: forall s (m :: * -> *) a.
                 (MonadState s m, Num a) =>
                 LensLike' ((,) a) s a -> a -> m a
          {-^ Subtract from the target of a numerically valued 'Lens' into your 'Monad''s
              state and return the result.
    
              When you do not need the result of the subtraction,
              ('Control.Lens.Setter.-=') is more flexible.
    
              @
              ('<-=') :: ('MonadState' s m, 'Num' a) => 'Lens'' s a -> a -> m a
              ('<-=') :: ('MonadState' s m, 'Num' a) => 'Control.Lens.Iso.Iso'' s a -> a -> m a
              @-}
          with (<-=) @Test @(StateT Test Identity) @Int
        (<<*=) :: forall s (m :: * -> *) a.
                  (MonadState s m, Num a) =>
                  LensLike' ((,) a) s a -> a -> m a
          {-^ Modify the target of a 'Lens' into your 'Monad''s state by multipling a value
              and return the /old/ value that was replaced.
    
              When you do not need the result of the operation,
              ('Control.Lens.Setter.*=') is more flexible.
    
              @
              ('<<*=') :: ('MonadState' s m, 'Num' a) => 'Lens'' s a -> a -> m a
              ('<<*=') :: ('MonadState' s m, 'Num' a) => 'Iso'' s a -> a -> m a
              @-}
          with (<<*=) @Test @(StateT Test Identity) @Int
        (Some hole fits suppressed; use -fmax-valid-hole-fits=N or -fno-max-valid-hole-fits)
    
    ```
    
    Which allows you to see at a glance what opaque operators like `(<<*=)`
    and `(<#=)` do.
    
    Reviewers: bgamari, sjakobi
    
    Reviewed By: sjakobi
    
    Subscribers: sjakobi, alexbiehl, rwbarton, thomie, carter
    
    Differential Revision: https://phabricator.haskell.org/D4848
    c4d98341
TcHoleErrors.hs 44.4 KB