No, we can't in general. With the exception of Control.Arrow (why that exception? No clue!) GHC's rewrite rules try very hard not to change the meaning of even bad user code. Yes, x <$ xs is supposed to be equivalent to fmap (const x) xs, and fmap is supposed to obey the functor laws. But neither of these is guaranteed, so a rewrite rule depending on these laws could change the meaning of user code.

On Apr 1, 2016 1:03 PM, "David Thomas" <davidleothomas@gmail.com> wrote:
If we provide (under whatever names) both the discard-value and the
require-unit versions, then at least for the inlined case we could
safely remove the overhead of void with a rewrite rule.

On Fri, Apr 1, 2016 at 8:12 AM, Bryan Richter <b@chreekat.net> wrote:
> On Fri, Apr 01, 2016 at 10:23:24AM -0400, davean wrote:
>> On Fri, Apr 1, 2016 at 1:13 AM, Erik de Castro Lopo wrote:
>>
>> > Which is the exact problem. I suspect that most people use `forM_`
>> > as "assume the action returns unit" rather than "assume the return
>> > value of the action is ignored".
>> >
>>
>> A review of my code shows I mostly use it as "assume the return value of
>> the action is ignored".
>>
>> Many things return data that is sometimes useful, but not always. Rarely do
>> they have an '_' variant or the like to allow you to be more specific.
>
> (emphasis added:)
>
>> I wouldn't find it to be overly onerous to throw "void" in in those
>> cases though.
>
> The first email in this thread hinted at another possible solution,
> with "unexpected behavior from... code that... has zero warning (even
> with -Wall)."
>
> Even if forM_ gets a new type, I don't think people will be much
> assisted with the new message:
>
>     Expected: IO ()
>     Actual: IO (IO Int)
>
> That message actually mixes two different problems. If the wisdom
> becomes "throw a void on it", which I've already seen suggested a few
> times, you easily get back to the original problem. Change the body of
> the lambda to
>
>     void $ maybe (HT.insert ht k v) (const $ abort k) <$> HT.lookup ht k
>     ^^^^^^^
>
> ...and now it *still* typechecks without warnings, but the original
> problem remains.
>
> Better, if possible, would be
>
>     Warning: Discarded monadic value [in forM_]
>
> Even that wouldn't catch my 'void $' above, but it also wouldn't
> be indirectly *suggesting* it as a solution.
>
> However, the thing Snoyberg raised about performance seems important.
> Busting the stack while trying to throw away variables seems
> unfortunate. Is Free the best counterexample to the "performance" of
> `t a -> (a -> m ()) -> m ()`? Does Free really matter? It's not
> exactly known for being a performant option.
>
> _______________________________________________
> Libraries mailing list
> Libraries@haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>
_______________________________________________
Libraries mailing list
Libraries@haskell.org
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries