Yesod is using first case as return type

193 Views Asked by At

thanks for your time:

The idea is that the request may include either

  • a apple with code ( represented in integer )
  • apples with codes

the handler will branch base on the input

  • if it is an apple then return a string
  • if it is apples ,then return a list of strings

the issue is , why Haskell compiler complains that ,it expects [string] instead of [[strings]] as return value ?

when I switch lines of

Apple code -> ["APPLE"++show code]
Apples codes -> map (\x -> ["APPLE"++ show x]) codes

into

Apples codes -> map (\x -> ["APPLE"++ show x]) codes
Apple code -> ["APPLE"++show code]

Now haskell is expecting [[string]] instead of [string] .

Is it Yesod make first case as DEFAULT return type of that handler ?

code of interest:

data Fruit = Apple Int
           | Apples [Int]

data Req = Req {
   apple :: Fruit
}
$(deriveJSON defaultOptions ''Req)

data App = App

mkYesod "App" [parseRoutes|
 /show ShowAppleR POST
|]

instance Yesod App where
  yesodMiddleware = defaultYesodMiddleware

postShowAppleR :: Handler Value
postShowAppleR =  do
  runReq <- requireCheckJsonBody :: Handler Req
  returnJson $
      case (apple runReq) of
        Apple code -> ["APPLE"++show code]
        Apples codes -> map (\x -> ["APPLE"++ show x]) codes

full code :

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes           #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeFamilies          #-}
module Main2 where

import Data.Aeson       hiding (json)
import Data.Monoid      ((<>))
import Data.Text        (Text, pack)
import GHC.Generics
import Data.ByteString.Lazy.Char8 (unpack)

import Language.Haskell.TH
import Data.Aeson.TH
import Data.Aeson.Types

import Yesod
import Network.Wai
import Network.Wai.Handler.Warp
import Network.HTTP.Types
import Network.Wai.Middleware.Cors


data Fruit = Apple Int
           | Apples [Int]

data Req = Req {
   apple :: Fruit
}
$(deriveJSON defaultOptions ''Req)

data App = App

mkYesod "App" [parseRoutes|
 /show ShowAppleR POST
|]

instance Yesod App where
  yesodMiddleware = defaultYesodMiddleware

postShowAppleR :: Handler Value
postShowAppleR =  do
  runReq <- requireCheckJsonBody :: Handler Req
  returnJson $
      case (apple runReq) of
        Apple code -> ["APPLE"++show code]
        Apples codes -> map (\x -> ["APPLE"++ show x]) codes


main :: IO ()
main =
  do
   app <- toWaiApp App
   run 8084 $ defaultMiddlewaresNoLogging
            $ cors (const $ Just $ simpleCorsResourcePolicy
                                    { corsOrigins = Nothing
                                    , corsMethods = ["OPTIONS", "GET", "PUT", "POST"]
                                    , corsRequestHeaders = simpleHeaders })
            $ app
1

There are 1 best solutions below

0
Shawn Zhang On BEST ANSWER

The root cause is that returnJson looks take the FIRST statement as return type. This issue can be solved by using two returnJson to wrap two return types.

postShowAppleR :: Handler Value
postShowAppleR =  do
  runReq <- requireCheckJsonBody :: Handler Req
  case (apple runReq) of
    Apples codes -> returnJson $ map (\x -> ["APPLE"++ show x]) codes
    Apple code -> returnJson $ ["APPLE"++show code]