How to write a family of printf functions (debug printing, etc.) in Haskell

This is a difficult problem more than a useful problem (I spent several hours on it). Given some features,

put_debug, put_err :: String -> IO ()
put_foo :: String -> StateT [String] m ()

I want to write a generic printf function, call it gprint so I can write

pdebug = gprint put_debug
perr = gprint put_err
pfoo = gprint put_foo

and then use pdebug, perrand pfoolike printf, for example,

pdebug "Hi"
pdebug "my value: %d" 1
pdebug "two values: %d, %d" 1 2

I am unable to find a sufficiently general class. My attempts were such as (for those who are familiar with printf, or Oleg's variational function method)

class PrintfTyp r where
    type AppendArg r a :: *
    spr :: (String -> a) -> String -> [UPrintf] -> AppendArg r a

or

class PrintfTyp r where
    type KRetTyp r :: *
    spr :: (String -> KRetTyp r) -> String -> [UPrintf] -> r

: r ( AppendArg), , instance PrintfTyp a, ( ).

, : , . , . !!

+5
3

, Text.Printf . , :

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}

-- To avoid having to write some type signatures.
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE ExtendedDefaultRules #-}

import Control.Monad.State
import Text.Printf

, printf, String, , .

gprint :: GPrintType a => (String -> EndResult a) -> String -> a
gprint f s = gprint' f (printf s)

class PrintfType (Printf a) => GPrintType a where
  type Printf a :: *
  type EndResult a :: *
  gprint' :: (String -> EndResult a) -> Printf a -> a

printf, g.

instance (PrintfArg a, GPrintType b) => GPrintType (a -> b) where
  type Printf (a -> b) = a -> Printf b
  type EndResult (a -> b) = EndResult b
  gprint' f g x = gprint' f (g x)

f:

instance GPrintType (IO a) where
  type Printf (IO a) = String
  type EndResult (IO a) = IO a
  gprint' f x = f x

instance GPrintType (StateT s m a) where
  type Printf (StateT s m a) = String
  type EndResult (StateT s m a) = StateT s m a
  gprint' f x = f x

, :

put_debug, put_err :: String -> IO ()
put_foo :: Monad m => String -> StateT [String] m ()

put_debug = putStrLn . ("DEBUG: " ++)
put_err   = putStrLn . ("ERR: " ++)
put_foo x = modify (++ [x])

pdebug = gprint put_debug
perr = gprint put_err
pfoo = gprint put_foo

main = do
  pdebug "Hi"
  pdebug "my value: %d" 1
  pdebug "two values: %d, %d" 1 2
  perr "ouch"
  execStateT (pfoo "one value: %d" 42) [] >>= print

:

DEBUG: Hi
DEBUG: my value: 1
DEBUG: two values: 1, 2
ERR: ouch
["one value: 42"]
+3

. , put_foo Text.Printf ( PrintfType, ). , , :

{-# LANGUAGE TypeFamilies #-} -- for ~ syntax
import Control.Monad.State
import Data.Default

-- copy and paste source of Text.Printf here

put_foo :: String -> StateT [String] m ()
put_foo = undefined

instance (Default a, Monad m, s ~ [String]) => PrintfType (StateT s m a) where
    spr s us = put_foo (spr s us) >> return def

put_debug put_err PrintfType , HPrintfType, String -> IO () .

pdebug  = funPrintf put_debug
perr    = funPrintf put_err
printf' = funPrintf putStr -- just for fun
pfoo    = printf
+1

I am not sure if the compiler can output this. How does he know that you expect the string to be printed in the context of the monad StateT, as opposed to accepting another argument in the monad (a ->).

You will probably need to introduce a method for displaying type checking when the argument list is complete. The easiest way is to simply wrap it in a function, so you write:

pdebug $ printf "%d %d %d" 1 2 3

And then it pdebugcan be polymorphic in the monad.

You can also download it to use a terminator, for example:

data Kthx = Kthx
printf "%d %d %d" 1 2 3 Kthx

But I can’t understand how now.

0
source

All Articles