Haskell Spock IO within GET Route ActionCtxT Error

247 Views Asked by At

I try to return a uuid within a route definition for a web app (Spock Webserver).

A route is pretty simple to define

get("PATH") $ do
 text "Hello World"

Now I try to return a uuid via nextRandom from the Data.UUID.V1 module. The function returns a IO(Maybe UUID) value.

So I thought, since I am in IO and work with another IO I have to do bind the value simply with <-, like so:

get ("id") $ do
    uuid<-nextUUID
    json . pack $ show $ uuid

But the compiler says no:

Couldn't match type ‘ActionCtxT ctx0 m0’ with ‘IO’
      Expected type: IO b0
        Actual type: ActionCtxT ctx0 m0 b0
    • In a stmt of a 'do' block: json . pack $ show $ uuid
      In the second argument of ‘($)’, namely
        ‘do { uuid <- nextUUID;
              json . pack $ show $ uuid }’

Why is it throwing that error? I could easily create the uuid with a simple print example, but within Spock I don't understand what the ActionCtxT does and why I can't do the uuid IO in it.

2

There are 2 best solutions below

1
On BEST ANSWER

So I thought, since I am in IO and work with another IO

That's the trouble here, when you're routing in Spock, you're not in IO. The error message tells you what context you're really in: ActionCtxT ctx0 m0. According to the docs, that's a monad transformer stack that bundles effects and state.

You can "lift" an IO computation into the right type using liftIO.

get ("id") $ do
    uuid <- liftIO nextUUID
    json . pack $ show $ uuid
0
On

Based on Libbys helpful answer I just added the catch for the Nothing of the Maybe UUID. Here the full programm:

{-# LANGUAGE OverloadedStrings #-}


module Main where

import Web.Spock hiding (head)
import Web.Spock.Config

import Data.UUID.V1
import Data.Pool
import Control.Monad.IO.Class
import Database.PostgreSQL.Simple
import Data.Aeson (Value(Null))

import qualified Network.HTTP.Types.Status as Http

type AppAction a = SpockActionCtx () Connection AppSession AppState a
data AppState = EmptyState
data AppSession = EmptySession


main :: IO ()
main =
  do pool<-createPool (connect (ConnectInfo "localhost" 5432 "" "" "envelopes") ) close 1 10 10
     spockCfg <- defaultSpockCfg EmptySession (PCPool pool) EmptyState
     runSpock 8080 (spock spockCfg app)

app :: SpockM Connection AppSession AppState ()
app = do 
    get ("json/id") $ do
      uuid<-liftIO nextUUID
      case uuid of
        Nothing -> do 
          setStatus Http.status500
          json Null
        Just x  -> json $ show x