Reputation:
I am fairly new to Haskell and Snap and I am working through my own project for a small web forum using snap. The problem I am having is in understanding the conflict and how to resolve for the following code.
handleCategoryAdd :: H ()
handleCategoryAdd = method POST (withLoggedInUser go)
where
go user = do
bs <- getParam "categoryName"
cN <- B.unpack $ Just bs
cD <- getParam "categoryDesc"
cT <- getCurrentTime
return (Db.saveCategory (Db.Category 1 cN cT "1" ))
redirect "/"
Gives me a type mismatch error as follows:
src\Site.hs:118:22:
Couldn't match expected type `ByteString'
with actual type `Maybe ByteString'
In the first argument of `unpack', namely `bs'
In a stmt of a 'do' block: cN <- unpack bs
In the expression:
do { bs <- getParam "categoryName";
cN <- unpack bs;
cD <- getParam "categoryDesc";
cT <- getCurrentTime;
.... }
Any advice to clear up the confusion would be much appreciated, I have been searching for some way to unwrap the maybe and just retrieve the bytestring but have been unsuccessful.
Many thanks!
Upvotes: 0
Views: 334
Reputation: 4984
A nice way of extracting a value from a Maybe a
type is to use the fromMaybe
function from the Data.Maybe
module. It's useful if you have a default value to use in the case of a Nothing
value in the Maybe
.
I will present an example in the IO monad rather than some Snap monad, in order to avoid pulling in the Snap libraries for the demo code to work, but it should work the same (aside from type signature changes) as long as the handler is in the same monad as the getParam
actions.
{-# LANGUAGE OverloadedStrings #-}
import Data.Maybe
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B
getParam1, getParam2 :: IO (Maybe ByteString)
getParam1 = return $ Just "Hello"
getParam2 = return $ Nothing
main :: IO ()
main = do
str1 <- getParam1 >>= return . fromMaybe "No value"
str2 <- getParam2 >>= return . fromMaybe "No value"
B.putStrLn str1
B.putStrLn str2
Since getParam1
and getParam2
are IO (Maybe ByteString)
, we know we need to use a monadic action to get at the Maybe ByteString
value inside.
By looking at the type signature of >>=
, which is m a -> (a -> m b) -> m b
, we can set type a
to be Maybe ByteString
and type b
to be IO ByteString
, which means we are looking to apply the bind with these specific types:
-- (Type of getParam1 >>= Type of our function to supply) -> Desired type
IO (Maybe ByteString) -> (Maybe ByteString -> IO ByteString) -> IO ByteString
With that in mind, we can look at the type of fromMaybe
:
fromMaybe :: a -> Maybe a -> a
We can partially apply fromMaybe with a default ByteString and get:
fromMaybe "No value" :: Maybe ByteString -> ByteString
Now we compose that function with return
to put the ByteString
back in IO
:
return . fromMaybe "No value" :: Maybe ByteString -> IO ByteString
This is the type we need to use >>=
on the result of getParam1
.
Since we are in a do
block in the IO
monad, we can extract the ByteString
for further use using the <-
binding arrow syntax:
str1 <- getParam1 >>= return . fromMaybe "No value"
At this point, you have a pure ByteString
in str1
to use as you see fit in the do
block.
Hopefully you find this explanation helpful!
Upvotes: 1
Reputation: 3766
Just
is a constructer for the type Maybe a
. getParam
, i'm pretty sure, returns Snap Maybe Bytestring
so bs
is of type Maybe Bytestring
. When you say Just bs
you then would have Maybe (Maybe Bytestring)
which is clearly what you don't want. You are looking for fromJust :: Maybe a -> a
but this function is dangerous because you haven't checked if getParam
has even succeeded and there is an input on on the html page with that name.
What you should do is use cases or >>=
.
Something like
bs <- getParam "categoryName"
case bs of
Nothing -> writeBS "failed"
Just b -> do
let unpacked = B.unpack b
-- ... Do more stuff
EDIT
It depends what you are trying to do. Bind can be useful and so can liftM
, lifM2
, liftM3
, etc.
In your case you may want liftM2. I don't know what your types are but I'll give a very simple example. Save you have have those 2 Bytestrings you could do something like this.
bs1 <- getParam "one"
bs2 <- getParam "two"
case (liftM2 append bs1 bs2) of
Nothing -> writeBS "Failed"
Just b -> writeBS b
Upvotes: 2