[web-devel] yesod in five minutes

Michael Snoyman michael at snoyman.com
Tue May 31 05:23:52 CEST 2011


On Tue, May 31, 2011 at 1:22 AM, George Colpitts
<george.colpitts at gmail.com> wrote:
> Hi
> I tried yesod in five minutes and I'm having a problem trying to add a date
> and time string that would update on refresh.
> I just want to add it to the simple page you get with yesod in 5 minutes. I
> can embed a date and time string that is the value of a Haskell variable but
> that won't refresh on update to the current date and time. My simplistic
> approach was to put a line in Handler/root.hs:
>
> currTimeAndDate = getClockTime
>
> but that won't compile:
>
> Handler/Root.hs:22:21:
>     No instance for (blaze-html-0.4.1.1:Text.Blaze.ToHtml
>                        (IO ClockTime))
>       arising from a use of `toHtml'
>     Possible fix:
>       add an instance declaration for
>       (blaze-html-0.4.1.1:Text.Blaze.ToHtml (IO ClockTime))
>     In the second argument of `(.)', namely `toHtml'
>     In the expression:
>         hamlet-0.8.2:Text.Hamlet.Quasi.htmlToHamletMonad . toHtml
>     In a stmt of a 'do' expression:
>         (hamlet-0.8.2:Text.Hamlet.Quasi.htmlToHamletMonad . toHtml)
>           currTimeAndDate
>
> I'm sure it would compile if I wrapped getClockTime with unsafePerformIO to
> get a String but then refresh of the page wouldn't give me  the current
> time. In addition it doesn't seem like the right way to do this.
> I'm sure my problem is due to my lack of knowledge of Haskell IO and monads
> which I do plan to learn thoroughly but right now I'd just like to get this
> to work. It seems like  I could do it with a form in Yesod but that seems
> like overkill and wouldn't have the look I want.
> I'm very impressed with the software, the book and the source code for the
> www.haskellers.com. It is a testimony to the quality of all three that I
> have got this far, particularly considering how little I know. Any help
> would be greatly appreciated.
> Sincerely
> George

Hi Geroge,

Firstly, the answer is to write something like:

    currTimeAndDate <- liftIO getClockTime

You can take that as magic if you like, but I'm guessing from the rest
of your email you actually care about understanding things. As you
guessed, what's going on here has to do with monads. In particular,
getClockTime is not a pure value, but rather an "IO action". In other
words, it's an instruction on *how* to produce a value. Another
example of this is the code: readFile "myfile.txt". The type of
readFile is:

    readFile :: FilePath -> IO String

which basically means "if you give me a file path, I'll give you the
instructions on how to generate a string." Once you are inside the IO
monad yourself, you can run these instructions, with code like:

    main = do
        string <- readFile "myfile.txt" -- notice the <-, not an equal sign
        putStrLn $ "Here's the file: " ++ string

There's a slight extra bit of complication in the code you're trying
to run here. The handler code lives in the Handler monad, *not* the IO
monad. However, a Handler is a MonadIO, which basically means it can
run any IO action. (This is called a monad transformer, but let's not
worry about that yet.) The function needed to convert from an IO
action to a Handler action is liftIO.

I hope that clarifies things a bit. My general recommendation about
monads is to not worry too much about that and just keep using the
language. Eventually, you'll start to see the inner workings
automatically, and you'll suddenly realize that monads are like
burritos[1]. But don't try to rush that intuition too much, it just
gets frustrating. If you want to study something, what worked for me
was trying to understand how the Maybe monad worked, but to each his
own.

Michael

[1] http://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/



More information about the web-devel mailing list