https://wiki.haskell.org/api.php?action=feedcontributions&user=Wyager&feedformat=atomHaskellWiki - User contributions [en]2021-09-24T19:21:47ZUser contributionsMediaWiki 1.27.4https://wiki.haskell.org/index.php?title=Nitpicks&diff=59987Nitpicks2015-08-20T05:20:30Z<p>Wyager: Wrong category</p>
<hr />
<div>[[Category:Language]]<br />
<br />
This page is for people to record nitpicks about the Haskell language.<br />
<br />
A "nitpick", in this case, is something that is annoying or could be improved, but is probably not important enough to justify the added complexity of tacking it on as an extension or breaking existing code.<br />
<br />
In other words, if we could go back in time and fix it before it happened, we probably would, but now it would probably be too onerous.<br />
<br />
Ideally, these nitpicks could help to inform future proposals or compatibility-breaking changes to the language. Even if they may be too onerous to change right now, it's possible that it would make sense to address them at some other time.<br />
<br />
If the nitpick has been discussed at length, please post a link to the discussion.<br />
<br />
== Syntax-related nitpicks ==<br />
<br />
* Re-naming <hask>data</hask>, <hask>newtype</hask>, and <hask>type</hask>. See https://mail.haskell.org/pipermail/haskell-cafe/2015-August/120724.html .<br />
<br />
== Semantics-related nitpicks ==<br />
<br />
== Base-related nitpicks ==<br />
<br />
* Re-naming <hask>fmap</hask> to <hask>map</hask>. This has been discussed at length; see http://stackoverflow.com/questions/6824255/whats-the-point-of-map-in-haskell-when-there-is-fmap/6824333 and https://mail.haskell.org/pipermail/haskell-prime/2006-August/thread.html</div>Wyagerhttps://wiki.haskell.org/index.php?title=Nitpicks&diff=59986Nitpicks2015-08-20T05:20:11Z<p>Wyager: Created page with "Category:Applications This page is for people to record nitpicks about the Haskell language. A "nitpick", in this case, is something that is annoying or could be improve..."</p>
<hr />
<div>[[Category:Applications]]<br />
<br />
This page is for people to record nitpicks about the Haskell language.<br />
<br />
A "nitpick", in this case, is something that is annoying or could be improved, but is probably not important enough to justify the added complexity of tacking it on as an extension or breaking existing code.<br />
<br />
In other words, if we could go back in time and fix it before it happened, we probably would, but now it would probably be too onerous.<br />
<br />
Ideally, these nitpicks could help to inform future proposals or compatibility-breaking changes to the language. Even if they may be too onerous to change right now, it's possible that it would make sense to address them at some other time.<br />
<br />
If the nitpick has been discussed at length, please post a link to the discussion.<br />
<br />
== Syntax-related nitpicks ==<br />
<br />
* Re-naming <hask>data</hask>, <hask>newtype</hask>, and <hask>type</hask>. See https://mail.haskell.org/pipermail/haskell-cafe/2015-August/120724.html .<br />
<br />
== Semantics-related nitpicks ==<br />
<br />
== Base-related nitpicks ==<br />
<br />
* Re-naming <hask>fmap</hask> to <hask>map</hask>. This has been discussed at length; see http://stackoverflow.com/questions/6824255/whats-the-point-of-map-in-haskell-when-there-is-fmap/6824333 and https://mail.haskell.org/pipermail/haskell-prime/2006-August/thread.html</div>Wyagerhttps://wiki.haskell.org/index.php?title=Performance/Monads&diff=59971Performance/Monads2015-08-12T20:17:15Z<p>Wyager: Example was incorrect. liftM2 is not equivalent to the other code.</p>
<hr />
<div>{{Performance infobox}}<br />
[[Category:Performance|Monads]]<br />
== Unroll your MTL stacks ==<br />
<br />
MTL is an excellent library for programming with monads. However stacked monad transformers do not inline well and the library is in need of an optimization pass. As a result, it can often impose a performance hit of up to 300% (your code will run up to three times slower). <br />
<br />
If you care about this, the best option is to flatten you stack of transformers into a single, hand unrolled monad. An extreme example follows.<br />
<br />
This is a typical MTL monad stack<br />
<br />
newtype DRM a = DRM {unDRM:: ErrorT Finish (RWS () DNA RNA) a}<br />
deriving (MonadState DNA, MonadWriter RNA, MonadError Finish, Monad)<br />
<br />
We can unroll it as follows:<br />
<br />
<haskell><br />
type DRM = DRMonad Finish DNA RNA<br />
newtype DRMonad e s w a = DRMonad {runDRMonad :: s -> (Either e a,s,w)}<br />
<br />
instance (Monoid m, Error e) => Monad (DRMonad e s w) where<br />
return x = DRMonad(\s -> (Right x, s, mempty))<br />
(>>= = bindDRMonad<br />
fail _ = DRMonad (\s->(Left e,s,mempty))<br />
<br />
{-# INLINE bindDRMonad #-}<br />
{-# INLINE bindDRMonad2 #-}<br />
bindDRMonad :: Monoid m => DRMonad a e s w -> (a -> DRMonad b e s w) -> DRMonad b e s w<br />
bindDRMonad m f = DRMonad$ \s -> case runDRMonad m s of<br />
(x',s',w) -><br />
bindDRMonad2 x' (s',w,f)<br />
bindDRMonad2 x' (s',w, f) = case x' of <br />
Left e -> (Left e, s', w)<br />
Right r -> case runDRMonad (f r) s' of<br />
(x'',s'',w') -><br />
(x'', s'', w `mappend` w')<br />
</haskell><br />
<br />
After this, you will also want to add the instances for MonadState, MonadWriter, etc.<br />
<br />
== Use [[Continuation | Continuation Passing Style]] ==<br />
<br />
It is well known that every monad can be "embedded" into the continuation passing monad, Cont. All that is necessary is to make the "answer type" of the Cont monad be the desired monad, e.g.<br />
<haskell><br />
type MCPS a = forall r. Cont (M r) a<br />
<br />
runMCPS m = runCont m return<br />
</haskell><br />
Note that this is just essentially just ContT M and indeed all of the below is just writing out the ContT implementation.<br />
<br />
Also note that M's (>>=) operation isn't used. It comes up when you implement the other operations M supports.<br />
<br />
In many cases, this will effectively avoid a layer of interpretation in much of the code using M. To see this, let's look at code that would benefit from using the Maybe monad.<br />
<haskell><br />
liftMaybe2 :: (a -> b -> Maybe c) -> Maybe a -> Maybe b -> Maybe c<br />
liftMaybe2 f a b =<br />
case a of<br />
Nothing -> Nothing<br />
Just a' -> case b of<br />
Nothing -> Nothing<br />
Just b' -> f a' b'<br />
</haskell><br />
<br />
This code screams for using Maybe as a monad in which case it will look like,<br />
<haskell><br />
liftMaybe2 f a b = do<br />
a' <- a<br />
b' <- b<br />
f a' b'<br />
</haskell><br />
<br />
One things to note about the original code is that it must constantly check the returned value even though a failure (Nothing) is most likely a rare occurrence, and further more it's possible that we will need to propagate a Nothing through an arbitrary large amount of code, though a lot of times this won't happen.<br />
<br />
Ideally, we want "failure free" code to run as normal and only deal with failure when it occurs and ''immediately'' fail in those cases rather than propagating the failure. This is what using continuation passing style gets us.<br />
<br />
Explicitly expanding Cont we get,<br />
<haskell><br />
newtype MaybeCPS a = MaybeCPS { unMaybeCPS :: forall r. (a -> Maybe r) -> Maybe r }<br />
<br />
runMaybeCPS m = unMaybeCPS m return<br />
<br />
-- The Cont definitions of return and (>>=)<br />
instance Monad MaybeCPS where<br />
return a = MaybeCPS (\k -> k a)<br />
MaybeCPS m >>= f = MaybeCPS (\k -> m (\a -> unMaybeCPS (f a) k))<br />
</haskell><br />
<br />
Note that this code is just normal CPS code and completely independent of the Maybe monad. There are no case analyses. So, "failure free" code will run as normal (CPS) code.<br />
<br />
How and why does this work? We're basically specializing <hask>>>=</hask>. Before, we had<br />
<haskell><br />
m >>= f = case m of<br />
Just a -> f a<br />
Nothing -> Nothing<br />
</haskell><br />
i.e. the monadic bind does a case analysis on how to proceed. In the CPS-representation, we're specializing bind to the two possible constructors:<br />
<haskell><br />
return' a := (return a >>=) = (Just a >>=) = \f -> f a<br />
mzero' := (mzero >>=) = (Nothing >>=) = \f -> Nothing<br />
</haskell><br />
and the case analysis is now "built-in" into <hask>return'</hask> and <hask>mzero'</hask>. In general, embedding a monad in <hask>Cont</hask> specializes <hask>>>=</hask> to its primitive operations, like for example <hask>mzero</hask> or <hask>get</hask>. This is close to specifying the operational semantics of <hask>>>=</hask> directly and can hence be used to implement the monad in the first place, too.<br />
<br />
Now we need to implement the operations.<br />
<haskell><br />
instance MonadPlus MaybeCPS where<br />
mzero = MaybeCPS (\_ -> Nothing) -- equivalent to MaybeCPS (Nothing >>=)<br />
m `mplus` n = case runMaybeCPS m of<br />
Nothing -> n<br />
Just a -> return a<br />
</haskell><br />
<br />
There are two things to note about this code. mplus is where we use the case analysis. mplus is the only place that we look at what was returned and to do it we have to actually run the computation. This means that we only deal with "effects" when we need to. Further, mzero discards its continuation. This is the typical pattern for aborting a computation in CPS and will lead to an ''immediate'' termination of the computation.<br />
<br />
MaybeCPS should be faster than using Maybe in most cases and should be almost a drop in replacement for Maybe. Unfortunately, this [http://www.mail-archive.com/haskell-cafe@haskell.org/msg65857.html may not necessarily be true]. Usually, using a CPS implementation would be a drop in replacement, but Maybe is not an abstract data type. Anyway, some results for a more complicated example are [http://r6.ca/blog/20071028T162529Z.html here].<br />
<br />
See also [http://wwwtcs.inf.tu-dresden.de/~voigt/mpc08.pdf Asymptotic Improvement of Computations over Free Monads].<br />
<br />
See also [[Codensity Monad]]</div>Wyager