[Haskell-cafe] Reducing the need for CPP (was: Monad of no `return` Proposal (MRP): Moving `return` out of `Monad`)

Ben Gamari ben at smart-cactus.org
Tue Oct 6 08:44:29 UTC 2015

Sven Panne <svenpanne at gmail.com> writes:

> 2015-10-05 17:09 GMT+02:00 Gershom B <gershomb at gmail.com>:
>> On October 5, 2015 at 10:59:35 AM, Bryan O'Sullivan (bos at serpentine.com)
>> wrote:
>> [...] As for libraries, it has been pointed out, I believe, that without
>> CPP one can write instances compatible with AMP, and also with AMP + MRP.
>> One can also write code, sans CPP, compatible with pre- and post- AMP. [...]
> Nope, at least not if you care about -Wall: If you take e.g. (<$>) which is
> now part of the Prelude, you can't simply import some compatibility module,
> because GHC might tell you (rightfully) that that import is redundant,
> because (<$>) is already visible through the Prelude. So you'll have to use
> CPP to avoid that import on base >= 4.8, be it from it Data.Functor,
> Control.Applicative or some compat-* module. And you'll have to use CPP in
> each and every module using <$> then, unless I miss something obvious.
> AFAICT all transitioning guides ignore -Wall and friends...
This is a fair point that comes up fairly often. The fact that CPP is
required to silence redundant import warnings is quite unfortunate.
Others languages have better stories in this area. One example is Rust,
which has a quite flexible `#[allow(...)]` pragma which can be used to
acknowledge and silence a wide variety of warnings and lints [1].

I can think of a few ways (some better than others) how we might
introduce a similar idea for import redundancy checks in Haskell,

 1. Attach a `{-# ALLOW redundant_import #-}` pragma to a definition,
        -- in Control.Applicative
        {-# ALLOW redundant_import (<$>) #-}
        (<$>) :: (a -> b) -> f a -> f b
        (<$>) = fmap

    asking the compiler to pretend that any import of the symbol did not
    exist when looking for redundant imports. This would allow library
    authors to appropriately mark definitions when they are moved,
    saving downstream users from having to make any change whatsoever.

 2. Or alternatively we could make this a idea a bit more precise,
        -- in Control.Applicative
        {-# ALLOW redundant_import Prelude.(<$>) #-}
        (<$>) :: (a -> b) -> f a -> f b
        (<$>) = fmap

    Which would ignore imports of `Control.Applicative.(<$>)` only if
    `Prelude.(<$>)` were also in scope.

 3. Attach a `{-# ALLOW redundancy_import #-}` pragma to an import,

        import {-# ALLOW redundant_import #-} Control.Applicative

        -- or perhaps
        import Control.Applicative
        {-# ALLOW redundant_import Control.Applicative #-}

    allowing the user to explicitly state that they are aware that this
    import may be redundant.

 4. Attach a `{-# ALLOW redundancy_import #-}` pragma to a name in an
    import list,

        import Control.Applicative ((<$>) {-# ALLOW redundant_import #-})

    allowing the user to explicitly state that they are aware that this
    imported function may be redundant.

In general I'd like to reiterate that many of the comments in this
thread describe genuine sharp edges in our language which have presented
a real cost in developer time during the AMP and and FTP transitions. I
think it is worth thinking of ways to soften these edges; we may be
surprised how easy it is to fix some of them.

- Ben

[1] https://doc.rust-lang.org/stable/reference.html#lint-check-attributes
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 472 bytes
Desc: not available
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20151006/42e097e1/attachment.sig>

More information about the Haskell-Cafe mailing list