0

When trying to load the following code into ghci:

import Control.Monad.Writer

newtype Writer w a = Writer { runWriter :: (a, w) } 

logNumber :: Int -> Writer [String] Int  
logNumber x = Writer (x, ["Got number: " ++ show x])  

multWithLog :: Writer [String] Int  
multWithLog = do  
    a <- logNumber 3  
    b <- logNumber 5  
    return (a*b)  

(it's from http://learnyouahaskell.com/for-a-few-monads-more)

I'm receiving this error:

writer1.hs:8:16: error:
    Ambiguous occurrence `Writer'
    It could refer to either `Control.Monad.Writer.Writer',
                             imported from `Control.Monad.Writer' at writer1.hs:1:1-27
                             (and originally defined in `Control.Monad.Trans.Writer.Lazy')
                          or `Main.Writer', defined at writer1.hs:3:1
Failed, modules loaded: none.

I have read through related questions on SO e.g. Ambiguous occurrence `Just' but I'm still struggling to see how to best apply this in my case. Beyond this specific problem it would be great if you could point to any conventions of how to avoid similar naming ambiguities.

Edit: As @chi pointed out in the comments I can skip newtype Writer w a = Writer { runWriter :: (a, w) } and then use a provided function writer instead of the Writer constructor. However this differs from the code in the book (which uses Writer) and when I call this in the REPL it shows me a different type WriterT:

*Main> logNumber 3
WriterT (Identity (3,["Got number: 3"]))

So I'm still not sure if using the writer function is how that code example is supposed to work.

Community
  • 1
  • 1
  • 4
    `Control.Monad.Writer` already defines a type named `Writer`; why are you defining another one? – chepner Dec 06 '16 at 14:01
  • 2
    That line (the definition of `Writer`) is just in the tutorial to explain how it's defined. You don't need to put it in your program, it's already in `Control.Monad.Writer`, as @chepner mentioned in the comment above. – Mike Harris Dec 06 '16 at 14:06
  • Hmm, if I delete `newtype Writer w a = Writer { runWriter :: (a, w) }` I get another error: `writer1.hs:6:15: error: * Data constructor not in scope: Writer :: (Int, [[Char]]) -> Writer [String] Int * Perhaps you meant one of these: `WriterT' (imported from Control.Monad.Writer), variable `writer' (imported from Control.Monad.Writer)` – andreas.thoelke Dec 06 '16 at 14:15
  • 1
    The library is a bit more general than your code. Instead of the constructor `Writer` you should use the plain function `writer`. – chi Dec 06 '16 at 14:23
  • 1
    `Writer` is an alias for `WriterT Identity` but GCHi expands the names. – Lee Dec 06 '16 at 14:47
  • Thanks @chi and @Lee! The example is working when using `writer`. I'm still slightly confused about why I now have to use `WriterT` which was not introduced in the book. – andreas.thoelke Dec 06 '16 at 14:54
  • 2
    Imagine you wanted to print to the console within `logNumber`. `putStrLn "message"` has type `IO ()` which is not compatible with `Writer [String] Int`. This is a general problem with monads where different monad type constructors (e.g. `IO`, `Maybe`, `Writer [String]`) cannot be automatically composed. One solution is to use _monad transformers_ which allow you to parameterise a monad by some inner monad. That is what `WriterT m w a` is, where `m` is the inner monad you can 'lift' values of `m a` from. `Identity` is a trivial base monad you can use if you don't need any other effects. – Lee Dec 06 '16 at 15:15
  • 1
    You will find monad libraries define all monads using the transformer versions (e.g. `ReaderT`, `WriterT`, `MaybeT`) and provide aliases where the inner monad is `Identity` when you don't need to combine effects. As you've found it does make things more complicated when learning about the base monad in question. – Lee Dec 06 '16 at 15:18
  • @andreas.thoelke LYaH was written before some changes were made to the base monad libraries. It is a little out of date in some details (although it remains an excellent introduction to the language as a whole). – chepner Dec 06 '16 at 16:01
  • 1
    I was confused as well when `WriterT` was introduced. Some years ago we used to have a simper `Writer` and then a generalization `WriterT` was added. After some time it felt silly to have the libraries carry two copies of the same thing, so the specific one (`Writer`) was redefined in terms of the more general one. The main downside is that with such approach one could not keep 100% compatibility, so that change did break some old code (as the one in your book). – chi Dec 06 '16 at 17:18
  • Thanks @chi that commend helped putting WriterT into perspective – andreas.thoelke Dec 06 '16 at 18:34

1 Answers1

1

If you want to follow the exercise in the book, and implement Writer yourself, then just remove the import Control.Monad.Writer from your code.

It looks though like the book switches to use Control.Monad.Writer in "Using do notation with Writer", so there you would just remove your own definition of Writer and use the import Control.Monad.Writer.

Control.Monad.Writer exports a type Writer, so that clashes with your own type.

If you would want to use method or types from Control.Monad.Writer anyhow, you could import it under a different name. E.g. import qualified Control.Monad.Writer as MW, then you could access the Writer defined there as MW.Writer, and your own as just Writer.

The Haskell wiki has pretty extensive documentation on how you can import modules in different ways (import just specific symbols, import a module without importing specific symbols, etc).

E4z9
  • 1,713
  • 9
  • 11
  • Thanks! I tried `import qualified Control.Monad.Writer as MW` and then use `MW.Writer`, but I'm still getting an error: `writer1.hs:8:15: error: Not in scope: data constructor MW.Writer Perhaps you meant one of these: MW.WriterT (imported from Control.Monad.Writer), variable MW.writer (imported from Control.Monad.Writer) Module Control.Monad.Writer does not export Writer.` – andreas.thoelke Dec 06 '16 at 17:20
  • 1
    True, in Control.MonadWriter, `Writer w a` is just a type alias for `WriterT w Identity a`. It is not its own type with its own data constructor, so you cannot create one with `Writer (x, ["Got number: " ++ show x])`. As Lee wrote you can use `writer` from `MonadWriter`: Since you have already heard of `Applicative`, I'd say it is similar to `pure`. Since `Writer w` (well, actually `WriterT w Identity`, but that is more an implementation detail for you right now), is an instance of `MonadWriter`, you can use `writer` to create one. – E4z9 Dec 06 '16 at 20:54
  • Thanks @E4z9, I think this explains it well. – andreas.thoelke Dec 06 '16 at 21:06
  • In principle it also is possible to take the direct way and create a `Writer [String] Int` by creating the actual `WriterT [String] Identity Int` with `WriterT $ Identity (x, ["Got number: " ++ show x])` (you need to import `Data.Functor.Identity` too, then). – E4z9 Dec 06 '16 at 21:09