Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Text.Printf with Data.Text?

Tags:

haskell

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.

like image 428
Skirmantas Kligys Avatar asked Jun 09 '12 20:06

Skirmantas Kligys


3 Answers

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.
like image 137
rp123 Avatar answered Oct 15 '22 18:10

rp123


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.
like image 3
John J. Camilleri Avatar answered Oct 15 '22 18:10

John J. Camilleri


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
like image 1
Thomas M. DuBuisson Avatar answered Oct 15 '22 16:10

Thomas M. DuBuisson