#5744: List layouts
---------------------------------+------------------------------------------
Reporter: nsch | Owner: nsch
Type: feature request | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 7.2.1
Keywords: | Os: Unknown/Multiple
Architecture: Unknown/Multiple | Failure: None/Unknown
Difficulty: Unknown | Testcase:
Blockedby: | Blocking:
Related: |
---------------------------------+------------------------------------------
Comment(by nsch):
Replying to [comment:15 NeilMitchell]:
> Note that you can already use {{{do}}} to define a {{{Monoid}}}:
Hi Neil,
In [http://vimeo.com/15465133 your own talk about your Shake library],
people are asking you (with good reason) why you're using a monadic
structure in your library and you respond with "it has great syntax and it
looks like a Make file" and that "'''shake is just a list''', it isn't a
monad in any remote way" (minute 24+ in the video). This is exactly why I
suggested a new keyword for a new non-monadic layout which represents a
simple list instead of wrapping everything (here: a monoid) into something
that looks like something completly different (a monad). "do" and my
proposed "be" (or your Monoid-wrap) do not behave the same way at all!
When I worked with the implementation of monad comprehensions we (me and
my team) chose a very simple way to make your monad comprehension
dependend on different type classes, depending on what statements your
were using in your comprehension. For example would you need a `MonadZip`
instance if your comprehension was using parallel statements, or a
`MonadGroup` instance if it was using a `group` statement – but if it
didn't a simple `Monad` instance for binding/returning values would be
completely sufficient. This was possible because the different statements
were compatible to each other, i.e. adding a parallel statement didn't
change the behaviour of the previous statements.
This is not the same with the "monoid-do" notation! My first attempt at
this proposal was a `do` that did basically what I have done with monad
comprehensions: Require a `Monoid` instance if we only had regular
expression statements (i.e. the `(>>)` case) and require a `Monad`
instance as soon as we used a binding statement (the `(>>=)` case):
{{{
{-# LANGUAGE MonoidDo #-}
newtype MyMonoid = MyMonoid Int
instance Monoid MyMonoid where
mempty = MyMonoid 0
mappend (MyMonoid x) (MyMonoid y) = MyMonoid (x+y)
mconcat = foldr mappend mempty
myMonoid :: MyMonoid
myMonoid = do
MyMonoid 1
MyMonoid 2
-- this works as expected for a monoid and results in "3"
newtype MyMonoidMonad a = MyMonoidMonad a
instance Monad MyMonoidMonad where
return x = MyMonoidMonad x
(MyMonoidMonad _) >> (MyMonoidMonad y) = MyMonoidMonad y
(MyMonoidMonad x) >>= f = f x
instance Monoid a => Monoid (MyMonoidMonad a) where
mempty = MyMonoidMonad mempty
mappend (MyMonoidMonad x) (MyMonoidMonad y) = MyMonoidMonad (x `mappend`
y)
mconcat = foldr (\(MyMonoidMonad x) (MyMonoidMonad y) -> MyMonoidMonad
(x `mappend` y)) mempty
-- For this data type a monoid-do notation would produce a different
result than
-- the monadic do-notation does!
myMonoidMonad :: MyMonoidMonad MyMonoid
myMonoidMonad = do
MyMonoidMonad (MyMonoid 1)
MyMonoidMonad (MyMonoid 2)
-- monoid result: 3
-- monad result: 2 !!
}}}
This example shows clearly: `do` should '''not''' be used for monoids as
the behaviour of `(>>)` and `mappend` is totally different! Yes, it is
possible to write those hacks (this is the reason why I came up with this
proposal), but it is '''not''' obvious to the user that you're not
actually doing something monadic in your library that way. Using `be {..}`
would be a lot cleaner (and more convenient too), both for the developers
and the end user.
--
Ticket URL: <http://hackage.haskell.org/trac/ghc/ticket/5744#comment:16>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
_______________________________________________
Glasgow-haskell-bugs mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-bugs