Haskell printf arguments as an array

I want to call the Text.Printf printf function with an array, but I cannot find a way. Here are two broken versions (actually the same idea).

import Text.Printf printfa :: (PrintfArg a) => String -> [a] -> String printfa format args = step (printf format) args where step :: (PrintfType r, PrintfArg a) => r -> [a] -> r step res (x:[]) = res x step res (x:xs) = step (res x) xs printfa' :: (PrintfArg a) => String -> [a] -> String printfa' format args = foldr (\arg p -> p arg) (printf format) args main = putStrLn $ printfa "%s %s" ["Hello", "World"] 

GHC Errors:

 printfa.hs:8:23: Couldn't match type `r' with `a1 -> r' `r' is a rigid type variable bound by the type signature for step :: (PrintfType r, PrintfArg a1) => r -> [a1] -> r at printfa.hs:8:5 The function `res' is applied to one argument, but its type `r' has none In the expression: res x In an equation for `step': step res (x : []) = res x printfa.hs:12:41: The function `p' is applied to one argument, but its type `String' has none In the expression: p arg In the first argument of `foldr', namely `(\ arg p -> p arg)' In the expression: foldr (\ arg p -> p arg) (printf format) args 

(Why: I am writing DSL and want to provide a printf function.)

+6
source share
3 answers

First, understand that PrintfArg a => [a] not a heterogeneous list. That is, although Int and String are instances of PrintfArg , [ 1 :: Int, "foo" ] not a valid construct.

So, if you defined the function :: PrintfArg a => String -> [a] -> String , then all arguments will have the same type.

To get around this, you can use existential quantification.

 {-# LANGUAGE ExistentialQuantification #-} import Text.Printf data PrintfArgT = forall a. PrintfArg a => P a printfa :: PrintfType t => String -> [ PrintfArgT ] -> t printfa format = printfa' format . reverse where printfa' :: PrintfType t => String -> [ PrintfArgT ] -> t printfa' format [] = printf format printfa' format (P a:as) = printfa' format as a main = do printfa "hello world\n" [] printfa "%s %s\n" [ P "two", P "strings"] printfa "%d %d %d\n" (map P $ [1 :: Int, 2, 3]) printfa "%d %s\n" [ P (1 :: Int), P "is the loneliest number" ] 

The reason your first solution fails is because you passed res for the step as an argument.

If you have foo :: Constraint a => a -> t , you guarantee that foo will work on all instances of Constraint . Although there is an instance of PrintfType that can take an argument, not all instances can. So your compiler error.

In contrast, when you have foo :: Constraint a => t -> a , you guarantee that foo will return any desired instance of Constraint . Again, the caller gets the choice of that instance. That's why my code works - when printfa' recurses, it requires that the recursive call return the value from the instance (PrintfArg a, PrintfType t) => a -> t .

For your second attempt, the compiler complains because foldr requires the accumulated value to be of the same type between iterations. The GHC notes that the accumulated value must be a function type (PrintfArg a, PrintfType t) => a -> t , because you use it in an iterated function. But you return the application value that it can determine is of type t . This means that t is equal to a -> t , which GHC does not like, because it does not allow infinite types. Therefore he complains.

If you want to use a fold, you can simply mask the battery type with Rank2Types or RankNTypes to keep the type constant between iterations.

 {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE RankNTypes #-} import Text.Printf data PrintfArgT = forall a. PrintfArg a => P a data PrintfTypeT = T { unT :: forall r. PrintfType r => r } printfa :: PrintfType t => String -> [ PrintfArgT ] -> t printfa format = unT . foldl (\(T r) (P a) -> T $ ra ) (T $ printf format) 
+14
source

I'm not sure if this is the minimum solution, but if you know the length of your vectors statically, you can use Vec indexes with type index and index Fun types.

 {-# LANGUAGE GADTs, TypeFamilies #-} import Text.Printf data Z data S n data Vec na where Nil :: Vec Z a Cons :: a -> Vec na -> Vec (S n) a type family Fn nba type instance Fn Z ba = a type instance Fn (S n) ba = b -> Fn nba -- in order to tell the compiler that we want to consider a function as a `Fn` newtype Fun nba = Fun (Fn nba) run :: Fun nba -> Vec nb -> a run (Fun f) v = case v of Nil -> f Cons b more -> run (Fun $ fb) more z :: Vec (S (SZ)) String z = Cons "foo" (Cons "bar" Nil) 

then you can do run (Fun $ printf "%s %s") z .

+2
source

Here is my.

 import Text.Printf (printf, PrintfType) printfList_ :: PrintfType t => String -> [String] -> Int -> t printfList_ string list n | n == 0 = printf string (list !! 0) | otherwise = (printfList_ string list (n - 1)) (list !! n) printfList :: String -> [String] -> String printfList string list = (printfList_ string list (length list - 1)) :: String 

Example:

 > printfList "%s%s%s" ["a","b","c"] "abc" 
0
source

Source: https://habr.com/ru/post/951759/


All Articles