Haskell Scotty Webserver send a text response - haskell

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.

Related

Couldn't match expected type ‘Request’ with actual type ‘[Char]’

I'm new to Haskel, I'm trying to run a simple example using http-conduit, the example is the one provided in their documentation.
However, when running the program I always get:
• Couldn't match expected type ‘Request’ with actual type ‘[Char]’
• In the first argument of ‘httpLBS’, namely
‘"http://httpbin.org/get"’
In a stmt of a 'do' block:
response <- httpLBS "http://httpbin.org/get"
In the expression:
do response <- httpLBS "http://httpbin.org/get"
putStrLn
$ "The status code was: " ++ show (getResponseStatusCode response)
print $ getResponseHeader "Content-Type" response
L8.putStrLn $ getResponseBody response
|
12 | response <- httpLBS "http://httpbin.org/get"
| ^^^^^^^^^^^^^^^^^^^^^^^^
I've tried to create a project both with cabal and stack, add http-conduit and aeson as dependencies, but still getting the error.
Shouldn't the url be implicitly converted to a Request?
I've tried to import Request and try to create a Request from the url, but it complains:
import Network.HTTP.Client.Request
<no location info>: error:
Could not load module ‘Network.HTTP.Client.Request’
it is a hidden module in the package ‘http-client-0.6.4.1’
You need to enable the OverloadedStrings extension [schoolofhaskell]. You can add the LANGUAGE pragma to the top of the file, so:
{-# LANGUAGE OverloadedStrings #-}
-- …
This extension will implicitly add fromString :: IsString a => String -> a to each string literal (not to be confused with an expression with type String). It makes it more convenient to work with string-like data such as for example Text.
Beware however that the conversion is done at runtime, so if not all Strings map to a (valid) Request object, then you will only see this when the Request is evaluated.
Shouldn't the url be implicitly converted to a Request?
In Haskell, there are no implicit conversions, you always convert data through functions. The OverloadedStrings simply adds a implicit function call to the literals, and thus this means that a string literal now can take as type any type that is a member of the IsString type class.

Problem decoding json from input args with Aeson

I'm trying to write a Haskell program that expects Json string provided as input. Expected Json is a list of Person objects. I want to handle a case when user did not provide input by defaulting to Json of empty list.
It seems like I'm running into type conversion issue between ByteString and String. I do have OverloadedStrings turned on but it doesn't seem to help here. Here's the simplified code.
{-# LANGUAGE OverloadedStrings #-}
import GHC.Generics
import System.Environment
import Data.Aeson
import Data.Maybe (fromMaybe, listToMaybe)
import Data.Text (Text)
data Person = Person { pName :: Text, pAge :: Int } deriving (Show,
Generic)
instance ToJSON Person
instance FromJSON Person
main :: IO (Maybe [Person])
main = do
args <- getArgs
arg1 <- pure $ (fromMaybe "[]" (listToMaybe args))
-- let arg1 = "[{\"pName\": \"James\", \"pAge\": 30}]"
return $ decode arg1 :: IO (Maybe [Person])
Error I'm getting is:
• Couldn't match type ‘[Char]’
with ‘Data.ByteString.Lazy.Internal.ByteString’
Expected type: Data.ByteString.Lazy.Internal.ByteString
Actual type: String
• In the first argument of ‘decode’, namely ‘arg1’
In the second argument of ‘($)’, namely ‘decode arg1’
In a stmt of a 'do' block:
return $ decode arg1 :: IO (Maybe [Person])
If I uncomment let arg1 to simulate what arg1 should be, then code compiles.
The problem is that getArgs returns a [String], not a [ByteString]. decode really wants a ByteString as input.
OverloadedStrings doesn't help with that; it only affects string literals in your code, not external inputs. That's why the version with the hardcoded argument (let arg1 = "[{\"pName\": \"James\", \"pAge\": 30}]") works: arg1 automatically becomes a ByteString to make decode arg1 work, but getArgs has an incompatible type.
One possible fix would be to encode the command line strings to bytes somehow, but there seems to be an easier alternative:
import System.Posix.Env.ByteString (getArgs)
which gives you a
getArgs :: IO [ByteString]
(I haven't actually tested this code; you may also have to use decodeStrict instead of decode.)

Haskell type error occurs when compiling two code snippets together that separately raise no error

I am having a type issue with Haskell, the program below throws the compile time error:
Couldn't match expected type ‘bytestring-0.10.8.2:Data.ByteString.Lazy.Internal.ByteString’ with actual type ‘Text’
Program is:
{-# LANGUAGE OverloadedStrings #-}
module Main where
...
import Control.Concurrent (MVar, newMVar, modifyMVar_, modifyMVar, readMVar)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Network.WebSockets as WS
import Data.Map (Map)
import Data.Aeson (decode)
...
application :: MVar ServerState -> WS.ServerApp
application state pending = do
conn <- WS.acceptRequest pending
msg <- WS.receiveData conn
-- EITHER this line can be included
T.putStrLn msg
-- OR these two lines, but not both
decodedObject <- return (decode msg :: Maybe (Map String Int))
print decodedObject
...
It seems to me that the basic issue is that putStrLn expects Text whereas decode expects Bytetring.
What I don't get is why I can run this section of the code:
T.putStrLn msg
Or I can run this section of the code:
decodedObject <- return (decode msg :: Maybe (Map String Int))
print decodedObject
But not both together.
What is the proper way to resolve this issue in the program?
I guess this is something like Type Coercion, or Type Inference, or what would be Casting in other languages. The problem is I don't know how to phrase the problem clearly enough to look it up.
It's as if msg can be one of a number of Types, but as soon as it is forced to be one Type, it can't then be another...
I'm also not sure if this overlaps with Overloaded strings. I have the pragma and am compiling with -XOverloadedStrings
I'm quite a newbie, so hope this is a reasonable question.
Any advice gratefully received! Thanks
This is because WS.receiveData is polymorphic on its return type:
receiveData :: WebSocketsData a => Connection -> IO a
it only needs to be WebSocketsData a instance, which both Text and ByteString are. So the compiler just infers the type.
I suggest you just assume it's a ByteString, and convert in Text upon the putStrLn usage.
Thanks to everyone for their advice. My final understanding is that any value in Haskell can be polymorphic until you force it to settle on a type, at which point it can't be any other type (stupid, but I hadn't seen a clear example of that before).
In my example, WS.receiveData returns polymorphic IO a where a is an instance of class WebsocketData which itself is parameterised by a type which can be either Text or Bytestring.
Aeson decode expects a (lazy) Bytestring. Assuming that we settle on (lazy) Bytestring for our a, this means the first line that I mentioned before needs to become:
T.putStrLn $ toStrict $ decodeUtf8 msg
to convert the lazy ByteString to a strict Text. I can do this so long as I know the incoming websocket message is UTF8 encoded.
I may have got some wording wrong there, but think that's basically it.

Haskell Spock IO within GET Route ActionCtxT Error

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.
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
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

Replace nested pattern matching with "do" for Monad

I want to simplify this code
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import Network.HTTP.Types
import Data.Text
getJSON :: String -> IO (Either String Value)
getJSON url = eitherDecode <$> simpleHttp url
--------------------------------------------------------------------
maybeJson <- getJSON "abc.com"
case maybeJson of
Right jsonValue -> case jsonValue of
(Object jsonObject) ->
case (HashMap.lookup "key123" jsonObject) of
(Just (String val)) -> Data.Text.IO.putStrLn val
_ -> error "Couldn't get the key"
_ -> error "Unexpected JSON"
Left errorMsg -> error $ "Error in parsing: " ++ errorMsg
by using do syntax for Monad
maybeJson <- getJSON "abc.com/123"
let toPrint = do
Right jsonValue <- maybeJson
Object jsonObject <- jsonValue
Just (String val) <- HashMap.lookup "key123" jsonObject
return val
case toPrint of
Just a -> Data.Text.IO.putStrLn a
_ -> error "Unexpected JSON"
And it gave me 3 errors:
src/Main.hs:86:19:
Couldn't match expected type `Value'
with actual type `Either t0 (Either String Value)'
In the pattern: Right jsonValue
In a stmt of a 'do' block: Right jsonValue <- maybeJson
src/Main.hs:88:19:
Couldn't match expected type `Value' with actual type `Maybe Value'
In the pattern: Just (String val)
In a stmt of a 'do' block:
Just (String val) <- HashMap.lookup "key123" jsonObject
src/Main.hs:88:40:
Couldn't match type `Maybe' with `Either String'
Expected type: Either String Value
Actual type: Maybe Value
Even when I replace
Just (String val) <- HashMap.lookup "key123" jsonObject
with
String val <- HashMap.lookup "key123" jsonObject
I'm getting another similar error about Either:
Couldn't match type `Maybe' with `Either String'
Expected type: Either String Value
Actual type: Maybe Value
In the return type of a call of `HashMap.lookup'
In a stmt of a 'do' block:
String val <- HashMap.lookup "key123" jsonObject
How do I fix those errors?
You can't easily simplify that into a single block of do-notation, because each case is matching over a different type. The first is unpacking an either, the second a Value and the third a Maybe. Do notation works by threading everything together through a single type, so it's not directly applicable here.
You could convert all the cases to use the same monad and then write it all out in a do-block. For example, you could have helper functions that do the second and third pattern match and produce an appropriate Either. However, this wouldn't be very different to what you have now!
In fact, if I was going for this approach, I'd just be content to extract the two inner matches into their own where variables and leave it at that. Trying to put the whole thing together into one monad just confuses the issue; it's just not the right abstraction here.
Instead, you can reach for a different sort of abstraction. In particular, consider using the lens library which has prisms for working with nested pattern matches like this. It even supports aeson nateively! Your desired function would look something like this:
decode :: String -> Maybe Value
decode json = json ^? key "key123"
You could also combine this with more specific prisms, like if you're expecting a string value:
decode :: String -> Maybe String
decode json = json ^? key "key123" . _String
This takes care of parsing the json, making sure that it's an object and getting whatever's at the specified key. The only problem is that it doesn't give you a useful error message about why it failed; unfortunately, I'm not good enough with lens to understand how to fix that (if it's possible at all).
So every line in a do expression for a Monad must return a value in that Monadic type. Monad is a typeclass here, not a type by itself. So putting everything in a do Monad is not really a sensible statement.
You can try your code with everything wrapped in a Maybe monad.
Assuming you've fetched your JSON value:
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import Network.HTTP
import qualified Data.Map as M
import Control.Applicative
import qualified Data.HashMap.Strict as HM
--------------------------------------------------------------------
main = do
maybeJson <- return $ toJSON (M.fromList [("key123","value")] :: M.Map String String)
ioVal <- return $ do -- The Maybe monad do expression starts here
maybeJson <- Just maybeJson
jsonObject <- case maybeJson of
Object x -> Just x
_ -> Nothing
val <- HM.lookup "key123" jsonObject
return val
putStrLn $ show ioVal
Once we start working in the Maybe monad, every expression must return a Maybe Something value. The way the Maybe monad works is that anything that is a Just something comes out as a pure something that you can work with, but if you get a Nothing, the rest of the code will be skipped and you'll get a Nothing.
This property of falling through is unique to the Maybe monad. Different monads behave differently.
You should read up more about Monads and the IO monad here: http://www.haskell.org/haskellwiki/Introduction_to_IO
You should read more about monads and what they really help you do:
http://learnyouahaskell.com/a-fistful-of-monads
(You should work through the previous chapters and then get to this chapter. Once you do, you'll have a pretty solid understanding of what is happening).
I also think your HTTP request is screwed up. Here's an example of a POST request that you can use.
import qualified Network.HTTP as H
main = do
postData <- return $ H.urlEncodeVars [("someVariable","someValue")]
request <- return $ H.postRequestWithBody "http://www.google.com/recaptcha/api/verify" "application/x-www-form-urlencoded" postData
putStrLn $ show request
-- Make the request
(Right response) <- H.simpleHTTP request
-- Print status code
putStrLn $ show $ H.rspCode response
-- Print response
putSrLn $ show $ H.rspBody response
UPDATED:
Use the following to help you get a JSON value:
import qualified Data.ByteString.Lazy as LC
import qualified Data.ByteString.Char8 as C
import qualified Data.Aeson as DA
responseBody <- return $ H.rspBody response
responseJSON <- return (DA.decode (LC.fromChunks [C.pack responseBody]) :: Maybe DA.Value)
You'll have to make a request object to make a request. There are quite a few helpers. I meant the post request as the most generic case:
http://hackage.haskell.org/package/HTTP-4000.0.5/docs/Network-HTTP.html
Since you are in the IO monad, all the <- are going to assume that you are dealing with IO operations. When you write
do
Right jsonValue <- maybeJson
Object jsonObject <- jsonValue
you are saying that jsonValue must be an IO action just like maybeJson. But this is not the case! jsonValue is but a regular Either value. The silution here would ge to use a do-block let instead of a <-:
do
Right jsonValue <- maybeJson
let Object jsonObject = jsonValue
However, its important to note that in both versions of your code you are using an irrecoverable error to abort your program if the JSON parsing fails. If you want to be able to collect errors, the basic idea would be to convert your values to Either (and then use the monad instance for Either to avoid having lots of nested case expressions)

Resources