I got sick of unpacking Data.Text instances all the time before printing them out for debugging and thought to just use Text.Printf for that. Unfortunately, I couldn't make it work:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Text
import Text.Printf
--instance PrintfArg Text where
-- toUPrintf = toUPrintf . unpack
main :: IO ()
main = do
let input :: Text = "abc"
printf "Input: %s\n" input
The error:
src/Main.hs:12:3:
No instance for (PrintfArg Text)
arising from a use of `printf'
Possible fix: add an instance declaration for (PrintfArg Text)
In a stmt of a 'do' block: printf "Input: %s" input
In the expression:
do { let input :: Text = "abc";
printf "Input: %s" input }
In an equation for `main':
main
= do { let input :: Text = ...;
printf "Input: %s" input }
After uncommenting the instance declaration:
src/Main.hs:7:7:
`toUPrintf' is not a (visible) method of class `PrintfArg'
src/Main.hs:7:19: Not in scope: `toUPrintf'
Any ideas?
EDITED
As suggested, tried TH, still no go:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
import Data.Text
import Language.Haskell.TH
import Text.Printf
runQ [d| instance PrintfArg Text where toUPrintf = toUPrintf . unpack|]
main :: IO ()
main = do
let input :: Text = "abc"
printf "Input: %s\n" input
Error:
src/Main.hs:9:40:
'toUPrintf' is not a (visible) method of class 'PrintfArg'
src/Main.hs:9:52: Not in scope: 'toUPrintf'
Help! It's amazing this doesn't work out of the box given all the advice to use Data.Text by default.
WARNING: text-format is unmaintained, no response from the author in 2 years. See other answers.
I'd look at the text-format package: it is similar to Text.Printf, but specifically designed for Data.Text.Lazy.
There are a few other advantages of text-format over Text.Printf:
The Buildable class is exposed, so it can be extended to support new parameter types.
It uses a simpler approach to varargs, which sidesteps problems one has in Text.Printf with accessing the return value.
It should be much faster, for several reasons:
it never converts to the inefficient String representation;
it doesn't build intermediate datatypes, unlike UPrintf in Text.Printf;
it uses the double-conversion package for rendering Double and Float, which is about 30 times faster than Prelude's methods.
Since this question was asked, the base and text libraries have been updated to support this. If you have base >= 4.7.0.0 and text >= 1.2.2.0, then the OP's MWE actually works:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Text
import Text.Printf
main :: IO ()
main = do
let input :: Text = "abc"
printf "Input: %s\n" input
Output:
$ ghci
GHCi, version 8.2.2: http://www.haskell.org/ghc/ :? for help
Prelude> :l test.hs
[1 of 1] Compiling Main ( test.hs, interpreted )
Ok, one module loaded.
*Main> main
Input: abc
*Main>
Leaving GHCi.
From the documentation:
The HPrintfType class provides the variable argument magic for hPrintf. Its implementation is intentionally not visible from this module.
While you could use TH to generate HPrintfType instances (because TH ignores export restrictions) the easiest solution is probably a printf' type function:
printt :: PrintType r => Text -> r
printt = printf . Data.Text.unpack
Another package worth checking out: formatting
Combinator-based type-safe formatting (like printf() or FORMAT) for Text.
Example:
format ("Person's name is " % text % ", age is " % hex) "Dave" 54
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.
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.
This code gives the following compile error:
Error:(17, 1) ghc: parse error (possibly incorrect indentation or mismatched brackets)
but if I remove
module Main where
it works. Since I'm just starting to use Haskell I would like to know why ?
module Main where
{-# LANGUAGE QuasiQuotes #-}
import Text.Hamlet (shamlet)
import Text.Blaze.Html.Renderer.String (renderHtml)
import Data.Char (toLower)
import Data.List (sort)
data Person = Person
{ name :: String
, age :: Int
}
main :: IO ()
main = putStrLn $ renderHtml [shamlet|
<p>Hello, my name is #{name person} and I am #{show $ age person}.
<p>
Let's do some funny stuff with my name: #
<b>#{sort $ map toLower (name person)}
<p>Oh, and in 5 years I'll be #{show ((+) 5 (age person))} years old.
|]
where
person = Person "Michael" 26
The line
{-# LANGUAGE QuasiQuotes #-}
is supposed to come on the first line in the program, before
module Main where
These language extensions are supposed to be meta information, external to the program itself (they can also be included as command line options to ghc).
I am new to Haskell, and am working on testing JSON serialization. Here is what the test case looks like:
{-# LANGUAGE OverloadedStrings #-}
module WetlandsTest where
import Control.Exception (evaluate)
import Test.Hspec
import Wetlands
main :: IO ()
main = hspec $ do
describe "wetlands" $ do
describe "spotting" $ do
it "returns a json encoded spotting" $ do
let record = spotting "Snowy Egret" "California" "low tide"
record `shouldBe` "{\"bird\":\"Snowy Eget\",\"state\":\"California\",\"meta\":\"low tide\"}"
Is there a way to write that in a more readable way? Maybe something along the lines of:
record `shouldBe` """
{"bird":"Snowy Eget","city":"California","meta":"low tide"}
"""
This isn't necessarily a multiline string, but if you prettified the JSON then it would be. Just wondering in general.
Just use the quasi-quotes extension and the string-qq package:
{-# LANGUAGE QuasiQuotes #-}
import Data.String.QQ
someString :: String
someString = [s|
This is"
some string with "" quotes and stuff"!
|]
With output:
*Main> someString
"This is\"\nsome string with \"\" quotes and stuff\"!\n"
Coming from C++, I'm used to be able to build simple forms of compile-time assertions, where I could emit warnings or errors during compilation if some simple conditions (e.g. over simple algebraic expressions) weren't met via use of template meta-programming and/or cpp(1)
For instance, if I wanted to make sure my program compiles only when Int has at least a certain minBound/maxBound range or alternatively, if a loss-free (as in reversible) conversion from Int64 to Int is possible with the current compilation target. Is this possible with some GHC Haskell extension? My first guess would have been to use TH. Are there other GHC facilities that could be exploited to this end?
Here's a generalized and slightly simplified version of Anthony's example:
{-# LANGUAGE TemplateHaskell #-}
module StaticAssert (staticAssert) where
import Control.Monad (unless)
import Language.Haskell.TH (report)
staticAssert cond mesg = do
unless cond $ report True $ "Compile time assertion failed: " ++ mesg
return [] -- No need to make a dummy declaration
Usage:
{-# LANGUAGE TemplateHaskell #-}
import StaticAssert
$(staticAssert False "Not enough waffles")
Using TH for this isn't too bad. Here is a module that defines the desired assertion as part of a vestigial declaration:
{-# LANGUAGE TemplateHaskell #-}
module CompileTimeWarning where
import Control.Monad (unless)
import Data.Int (Int64)
import Language.Haskell.TH
assertInt = let test = fromIntegral (maxBound::Int) == (maxBound::Int64)
in do unless test $ report True "Int is not safe!"
n <- newName "assertion"
e <- fmap NormalB [|()|]
return $ [FunD n [Clause [] e []]]
Using the assertion involves a top-level declaration that isn't used for anything other than the assertion:
{-# LANGUAGE TemplateHaskell #-}
import CompileTimeWarning
$(assertInt)