Again the n00b here: trying Warp and WAI with the following code as in the documentation.
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}
import Network.Wai
import Network.HTTP.Types
import Network.Wai.Handler.Warp (run)
app3 :: Application
app3 request respond = respond $ case rawPathInfo request of
"/" -> index
"/raw/" -> plainIndex
_ -> notFound
plainIndex :: Response
plainIndex = responseFile
status200
[("Content-Type", "text/plain")]
"index.html"
Nothing
notFound :: Response
notFound = responseLBS
status404
[("Content-Type", "text/plain")]
"404 - Not Found"
Running plainIndex in GHCi returns:
<interactive>:12:1: error:
• No instance for (Show Response) arising from a use of ‘print’
• In a stmt of an interactive GHCi command: print it
*Main> :r
[1 of 1] Compiling Main ( WAI.hs, interpreted )
Two questions in one: can you help me out fixing this, and in exension to that: I am the only one frequently encountering issues like this when following documentation examples?
Running plainIndex in GHCi, GHCi tries to compute the Response and then print it in the terminal. A Show instance defines how a given type should be represented as a String. The library authors have chosen not to provide a Show instance for Response, probably to separate its representation from its interface.
Various parts of a response have Show instances, so you can use accessors provided by Wai:
> responseStatus plainIndex
> responseHeaders plainIndex
More documentation for Response.
Related
I'm getting to grips with writing an API in Haskell using Scotty. My files are provided below. My questions are:
In the routes definition, I'm extracting from liftIO whatsTheTime in a do block. This works, but it seems verbose. Is there a nicer syntax?
In the whatsTheTime definition, I'm needing to do fromString. I'd have thought OverloadedString would take care of that, but that's not the case. I'd really appreciate it if somebody pointed out why it doesn't work without fromString.
In a stack project, if I need a directive like OverloadedStrings, do I need to include it every file that needs it, or just at the top of the main entrypoint?
Api.hs:
{-# LANGUAGE OverloadedStrings #-}
module Api
( whatsTheTime
) where
import Data.Time (getCurrentTime)
import Web.Scotty
import Data.String
whatsTheTime :: IO (ActionM ())
whatsTheTime = do
time <- getCurrentTime
return $ text $ fromString ("The time is now " ++ show time)
Main.hs:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
module Main where
import Api
import Web.Scotty
import Control.Monad.IO.Class
routes = do
get "/" $ do
res <- liftIO whatsTheTime
res
main :: IO ()
main = do
putStrLn "Starting server..."
scotty 3000 routes
(1) This:
do
res <- liftIO whatsTheTime
res
Desugars to this:
liftIO whatsTheTime >>= \ res -> res
If you look at the type of \ m -> m >>= id:
(Monad m) => m (m a) -> m a
That’s exactly the type of join (Hoogle), so you can use:
get "/" $ join $ liftIO whatsTheTime
join is a common idiom for “execute this action which returns an action, and also execute the returned action”.
(2) OverloadedStrings is for overloading of string literals. You have an overloaded literal "The time is now ", but you constrain it to be of type String by using it as an operand of (++) with a String (the result of show time). You can pack the result of show time as a Text instead using fromString or Data.Text.pack:
import Data.Monoid ((<>))
import qualified Data.Text as Text
-- ...
return $ text $ "The time is now " <> Text.pack (show time)
(3) LANGUAGE pragmas operate per file; as #mgsloan notes, you can add OverloadedStrings to the default-extensions: field of your library or executable in your .cabal file.
I am following the basic dispatching section of wai application.
I am able to catch the url parameter. How can I perform IO operation using these params.
I would like to use runCommand of System.Process to execute a system command using these parameters.
:t runCommand give
runCommand :: String -> IO ProcessHandle
my Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Control.Monad
import Network.Wai
import Network.HTTP.Types
import Network.Wai.Handler.Warp (run)
import Data.ByteString
import Control.Monad
import System.Process
import qualified Data.ByteString.Lazy.Char8 as L8
main :: IO ()
main = do
run 8080 app
app :: Application
app request respond = respond $ case rawPathInfo request of
"/" -> indexHtml
"/wake" -> wakeMeUP request
_ -> fourNotFour
indexHtml :: Response
indexHtml = responseFile
status200
[("Content-Type","text/html")]
"index.html"
Nothing
wakeMeUP :: Request -> Response
wakeMeUP request =
let query = queryString request
hour = join $ lookup "hour" query
min = join $ lookup "min" query
--I would like to use runCommand "using hour and min variables"
in responseLBS
status200
[("Content-Type","text/plain")]
"Alarm set at...to be coded later"
fourNotFour :: Response
fourNotFour = responseLBS
status404
[("Content-Type","text/plain")]
"404 not found"
Your design prevents it, because of how you have written app,
app request respond = respond $ case rawPathInfo request of
which says that you immediately respond. Note the type of Application:
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
Since the result type has an IO, you have the opportunity to do I/O before yielding a value. So you could:
app request respond = do
whateverResult <- runCommand "whatever"
respond $ ...
(You could also do it afterward, at least according to the types:
app request respond = do
rcvd <- respond $ ...
runCommand "whatever"
return rcvd
Though that's a bit odd to do for the continuation-passing idiom being used here (the (a -> b) -> b pattern in the Application type). It means that the command will be run after everything else, for some definition of "everything else" that we can't know without reading the wai source.)
Anyway, you probably don't want the command to be run inside app, but rather in wakeMeUp, which means you need to change some types around. In particular,
wakeMeUp :: Request -> IO Response
-- ^^
and suitably monadify the function. Then your app needs to not call respond immediately, so you can say
app request respond =
response <- case rawPathInfo request of
"/" -> return indexHtml
-- ^^^^^^
"/wake" -> wakeMeUp request
-- no change, because wakeMeUp now has an IO return type
...
respond response
If this is gibberish to you, it's time to do some monad tutorials. Or if you just want to get the damn thing working, I recommend Dan Piponi's The IO Monad For People who Simply Don't Care. Happy hacking!
I get a GET request and want to send a text message as a response to it.I have the following code but am getting the following error
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Network.Wai.Middleware.RequestLogger
import Data.Monoid (mconcat)
main = scotty 4000 $ do
middleware logStdoutDev
get "/" $ do
beam<- param "q"
text $ "The message which I want to send"
The error I am getting when I try to run the server is
No instance for (Parsable t0) arising from a use of ‘param’
The type variable ‘t0’ is ambiguous
Note: there are several potential instances:
instance Parsable () -- Defined in ‘scotty-0.9.1:Web.Scotty.Action’
instance Parsable Bool
-- Defined in ‘scotty-0.9.1:Web.Scotty.Action’
instance Parsable Data.ByteString.Lazy.Internal.ByteString
-- Defined in ‘scotty-0.9.1:Web.Scotty.Action’
...plus 9 others
In a stmt of a 'do' block: beam <- param "q"
In the second argument of ‘($)’, namely
‘do { beam <- param "q";
text $ "The message I want to send" }’
In a stmt of a 'do' block:
get "/"
$ do { beam <- param "q";
text $ "The message I want to send" }
param has type Parsable a => Text -> ActionM a. As you can see, a is polymorphic and only requires an instance of Parsable a. It's the return type as well. But as you can see in the documentation Parsable has several instances available! GHC doesn't know what you want, hence the ambiguous error. You will need to specify a type annotation letting GHC know what you want. But anyway, let's write the code
beam <- param "q" :: ActionM Text
should do the trick. Now beam should have be a Text. But maybe you are expecting your parameter to be an integer so something like
beam <- param "q" :: ActionM Integer
can work as well.
Trying to make a HTTP request, I got the error:
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Conduit -- the main module
-- The streaming interface uses conduits
import Data.Conduit
import Data.Conduit.Binary (sinkFile)
import qualified Data.ByteString.Lazy as L
import Control.Monad.IO.Class (liftIO)
main :: IO ()
main = do
simpleHttp "http://www.example.com/foo.txt" >>= L.writeFile "foo.txt"
The error is:
Couldn't match type `L.ByteString'
with `bytestring-0.10.0.2:Data.ByteString.Lazy.Internal.ByteString'
Expected type: bytestring-0.10.0.2:Data.ByteString.Lazy.Internal.ByteString
-> IO ()
Actual type: L.ByteString -> IO ()
In the return type of a call of `L.writeFile'
In the second argument of `(>>=)', namely `L.writeFile "foo.txt"'
In a stmt of a 'do' block:
simpleHttp "http://www.example.com/foo.txt"
>>= L.writeFile "foo.txt"
I can't figure out how to solve it because the text of the error doesn't really make sense.
You have two conflicting versions on bytestring package. Try ghc-pkg list bytestring. I'd suggest you to cabalize your code and use cabal sandbox.
See also "Couldn't match expected type with actual type" error when using Codec.BMP
BTW, the error message should be better in ghc-7.8, see https://ghc.haskell.org/trac/ghc/ticket/8278
Did lot of packing, unpacking and etc playing with Strings and Texts and still stuck,
the goal is simple as hello world with extra request info concatenated:
{-# LANGUAGE OverloadedStrings #-}
import Network.Wai
import Network.Wai.Handler.Warp (run)
import Network.HTTP.Types (statusOK)
import qualified Data.ByteString.Lazy as L
import Data.ByteString.Char8 (unpack)
import qualified Data.Text.Lazy as T
application req = do
return $ responseLBS statusOK [("Content-Type", "text/plain")]
$ L.pack $ "Hello World" ++ (unpack $ rawPathInfo req)
main = run 3000 application
produces:
Couldn't match expected type `GHC.Word.Word8'
against inferred type `Char'
Expected type: [GHC.Word.Word8]
Inferred type: [Char]
In the second argument of `(++)', namely
`(unpack $ rawPathInfo req)'
In the second argument of `($)', namely
`"Hello World" ++ (unpack $ rawPathInfo req)
Eagerly need hint how to make it work.
Use Data.ByteString.Lazy.Char8.pack instead of L.pack.
That's a lot of packing and unpacking. I don't have wai installed, so I can't test, but something like this should be simpler:
application req = do
return $ responseLBS statusOK [("Content-Type", "text/plain")]
$ L.append "Hello World" $ rawPathInfo req
i.e. simply use ByteString's append rather than String's (++).