Is jsonToRepJson broken? - haskell

I'm just starting doing some Yesod + Haskell stuff.
Is jsonToRepJson broken or something?
I made this code below but I always get an error in jsonToRepJson part.
It seems it doesn't get the expected type?
Any help would be great! Thanks :3
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
import Yesod
import Data.Text
data APP = APP
instance Yesod APP
mkYesod "APP" [parseRoutes|
/ TestR GET
|]
getTestR :: Handler RepJson
getTestR = jsonToRepJson $ object ["test".= ("test"::Text)]
main::IO()
main = warpDebug 3001 APP
this is what I get when I use runhaskell
api.hs:18:12:
Couldn't match expected type `RepJson' with actual type `Value'
Expected type: Handler RepJson
Actual type: HandlerT APP IO Value
In the expression:
jsonToRepJson $ object ["test" .= ("test" :: Text)]
In an equation for `getTestR':
getTestR = jsonToRepJson $ object ["test" .= ("test" :: Text)]

You must convert your value toJSON.
Eg.:
jsonToRepJson $ object [("result", toJSON resultValue)]
:)
You can read about that change in Yesod 1.2

What I did is, I used the TypeContent handler.
If I understood correctly what I've read, repSelect allows us to easily handle what type of data representation the client asks for.
It reads the request header and checks if it asks for JSON, then it will spit out JSON data, if it needs HTML it will then give the HTML page. Providing that you yourself had added the specific data needed using providRep.
Here's my code.
mkYesod "APP" [parseRoutes|
/ TestR GET
|]
getTestR::Handler TypedContent
getTestR = do
selectRep $ do
provideRep $ jsonToRepJson $ object $ (["test" .= ("test"::Text)])
main::IO()
main = warpDebug 3001 APP

Related

Evaluation of template haskell in Yesod

While going through the examples of the Yesod Book, I'm running into an issue with the following snippet:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
import Data.Text (Text)
import qualified Data.Text as T
import Yesod
data App = App
instance Yesod App
mkYesod "App" [parseRoutes|
/person/#Text PersonR GET
/year/#Integer/month/#Text/day/#Int DateR
/wiki/*Texts WikiR GET
|]
getPersonR :: Text -> Handler Html
getPersonR name = defaultLayout [whamlet|<h1>Hello #{name}!|]
handleDateR :: Integer -> Text -> Int -> Handler Text -- text/plain
handleDateR year month day =
return $
T.concat [month, " ", T.pack $ show day, ", ", T.pack $ show year]
getWikiR :: [Text] -> Handler Text
getWikiR = return . T.unwords
main :: IO ()
main = warp 3000 App
(It's on page 124 of 598; route arguments)
The instance declaration on line 11 raises the following error:
YesodRouteParams.hs:11:10: error:
• No instance for (RenderRoute App)
arising from the superclasses of an instance declaration
• In the instance declaration for ‘Yesod App’
|
11 | instance Yesod App
|
It can be fixed by moving that line below the mkYesod block, where routes are defined.
I'm trying to understand why that is. Does it mean that Template Haskell evaluation at compile time happens simultaneously with the written code evaluation?
I ask because in Crystal, for example, macros are expanded before anything else. So the order of things doesn't really matter in a file (or app). But by the looks of it, they do in Haskell. Or is there another explanation?
This was because of a change made in GHC 9.0.1. From the release notes:
Breaking change: Template Haskell splices now act as separation points between constraint solving passes. It is no longer possible to use an instance of a class before a splice and define that instance after a splice. For example, this code now reports a missing instance for C Bool:
class C a where foo :: a
bar :: Bool
bar = foo
$(return [])
instance C Bool where foo = True
If you were to downgrade to GHC 8.10.7, you'd see that your code would then work as you wrote it.
I opened https://github.com/yesodweb/yesodweb.com-content/pull/269 to fix the examples in the book.

QuasiQuotes with OverloadedLabels

I'm trying to use OverloadedLabels with QuasiQuotes from here package. Using plain lenses works but #foo fails with parsing error during compilation. So does field #"foo". Is there a deeper reason this will not work or could it be be a bug in here's interpolated parser?
{-# language DataKinds #-}
{-# language DeriveGeneric #-}
{-# language DerivingStrategies #-}
{-# language OverloadedLabels #-}
{-# language OverloadedStrings #-}
{-# language TypeApplications #-}
{-# language QuasiQuotes #-}
import Control.Lens
import Data.Text (Text)
import qualified Data.Text.IO as T
import Data.Generics.Product
import Data.Generics.Labels
import Data.String.Here
import GHC.Generics (Generic)
data Test = Test
{ name :: Text
} deriving stock (Eq, Show, Generic)
_name :: Lens' Test Text
_name f (Test a) = fmap (\a' -> Test a') (f a)
t :: Test
t = Test "test"
test :: IO ()
test = do
-- ok
T.putStrLn $ t ^. field #"name"
T.putStrLn $ t ^. #name
putStrLn [i|${t ^. _name}|]
-- parse error
putStrLn [i|The name is ${t ^. field #"name"}|]
putStrLn [i|The name is ${t ^. #name}|]
Error for #name:
test.hs:36:12: error:
• Exception when trying to run compile-time code:
Failed to parse interpolated expression in string: The name is ${t ^. #name}
(line 1, column 25):
0
SrcLoc "" 1 6
Parse error in expression: t ^.
CallStack (from HasCallStack):
error, called at src/Data/String/Here/Interpolated.hs:64:33 in here-1.2.13-HU0AD0x0dD36rY9YuL1gwE:Data.String.Here.Interpolated
Code: Language.Haskell.TH.Quote.quoteExp
i "The name is ${t ^. #name}"
• In the quasi-quotation: [i|The name is ${t ^. #name}|]
|
36 | putStrLn [i|The name is ${t ^. #name}|]
|
It looks like haskell-src-meta doesn’t support OverloadedLabels yet. The haskell-src-exts parser has an OverloadedLabels case, but haskell-src-meta doesn’t have a case for it in the ToExp instance for Exp. I guess the “unsupported” error message from haskell-src-meta is getting swallowed by the error handling in here.
Just in case anyone else stumbles upon this, I've made a PR to fix this in haskell-src-meta: https://github.com/DanBurton/haskell-src-meta/pull/19

Empty do block yesod after creating new handler

I try to create a new route in yesod / haskell with a handler called state, but I get the error empty 'do' block
The steps to reproduce are the following:
Create new yesod application: stack new haskellYesod yesodweb/simple
yesod add-handler for adding a new handler with the params:
Name of route: State
Route Pattern: /state/
Methods: GET
Add following code in src/Handler/State.hs
module Handler.State where
import Import
getStateR :: Handler Html
getStateR = do
defaultLayout $ do
$(widgetFile "bla")
Create simple HTML site templates/bla.hamlet:
<h1> BLA!
Start server with stack exec yesod devel
After that I get the error:
src/Handler/State.hs:7:21: error: Empty 'do' block
|
7 | defaultLayout $ do
What's happening is that without the TemplateHaskell extension, the syntax $(...) isn't recognized as intended. Instead, it's parsed as a do-block followed by the operator $ and then the expression (...), as if you'd written:
getStateR = do
defaultLayout $ (do $ widgetFile "bla")
^^ empty do-block
You'd see the same problem with the standalone program:
main = do
$(thiswontwork)
which is parsed as main = do $ thiswontwork and generates an Empty 'do' block message, too.
Adding {-# LANGUAGE TemplateHaskell #-} to the top of the State.hs file is enough to fix the problem.
The problem was missing extensions. After adding the following, the error disappeared:
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}

Navigating Haskell Servant API with Lucid WebUI

I've learnt that I can define my API with servant and Lucid in the following way:
type ClientAPI =
"users" :> Get '[HTML] (Html ())
:<|> "userdata" :> Get '[HTML] (Html ())
Then if I want to add a link to one of the endpoints in my HTML, I can use "a_" function provided by Lucid, e.g.
a_ [href_ "users"] "Show users"
The problem I have with this approach is that I need to repeat the endpoint's name twice. "users" occurs both in API definition and a_ tag. As a result if I change it in one place, the other one stops working immediately.
Is there a way to define a single symbol that could be used in both places instead ? Something like:
data MySites = UserSite | UserDataSite -- potentially more
type ClientAPI' =
UserSite :> Get '[HTML] (Html ())
-- ......
let html =
...
a_ [href_ UserSite] "Show users"
...
If you just want to abstract over the string "user", you can do this with a type alias, and then use GHC.TypeLits.symbolVal to get the string at the value level:
{-# LANGUAGE DataKinds #-}
import Data.Proxy
import GHC.TypeLits
type UserSite = "user"
html = ... href_ (symbolVal (Proxy :: Proxy UserSite)) ...
You can also make the symbolVal call a bit shorter by defining a helper with AllowAmbiguousTypes:
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
urlpath :: forall s . KnownSymbol s => String
urlpath = symbolVal (Proxy :: Proxy s)
html = ... href_ (urlpath #UserSite) ...
(I actually thought something like it was in the standard library somewhere but I can't find it.)

Couldn't match expected type `HandlerT...' with actual type `IO String'

Trying to make a yesod app (without stack, and yesod init) and when ever I compile it I run into this monad error. I know these issues are common, but I've never seen anyone ask this question with these specific types (i.e. HandlerT and IO String). Here's all my code, so I think it should be easy enough for someone to test this. Also I'm using GHC 7.10.3, which is a little older, but I don't think my issue has anything to do with the compiler version.
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.10.3
Here's the compiler error message:
$ ghc -o yesod_test yesod_monad_test.hs
[1 of 1] Compiling Main ( yesod_monad_test.hs, yesod_monad_test.o )
yesod_monad_test.hs:25:15:
Couldn't match expected type `HandlerT HelloWorld IO Text'
with actual type `IO String'
In a stmt of a 'do' block: snippet <- readFile temp
In the expression:
do { let temp = "posts/" ++ title ++ ".html";
snippet <- readFile temp;
defaultLayout
((asWidgetT . toWidget) (toHtml (preEscapedText snippet))) }
And here's my code.
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
import Yesod
import Network.Wai (pathInfo, rawPathInfo, requestMethod, responseLBS)
import Data.Text (Text)
import Text.Blaze (preEscapedText)
import Control.Exception (IOException, try)
import Control.Monad (when)
data HelloWorld = HelloWorld
mkYesod "HelloWorld" [parseRoutes|
/post/#String PostR GET
|]
instance Yesod HelloWorld
getPostR :: String -> Handler Html
getPostR title = do
let temp = "posts/" ++ title ++ ".html"
snippet <- readFile temp
defaultLayout [whamlet|#{preEscapedText snippet}|]
main :: IO ()
main = warp 3000 HelloWorld
I'm surprised that this code doesn't work, since I believe I'm following the tutorial (http://www.yesodweb.com/book/routing-and-handlers#routing-and-handlers_overlap_checking) pretty closely, though I'm using Strings over the Text type.
Thank you.
#Alec's answer was basically correct. Thanks!
Basically, #amalloy is telling you to replace snippet <- readFile temp with snippet <- liftIO $ readFile temp.

Resources