I have the following code that is supposed to format a telephone number. (printf
is from Text.Printf, splitPlaces
is from Data.List.Split).
prettyPrint :: String -> String
prettyPrint phoneNumber = printf "(%s) %s-%s" part1 part2 part3
where [part1, part2, part3] = splitPlaces [3, 3, 4] phoneNumber
What I'm looking for is an operator that allows the following way of writing the function:
prettyPrint = printf "(%s) %s-%s" <operator> splitPlaces [3, 3, 4]
Does such an operator exist? Can it exist at all?
Assuming this operator should pop off elements from a list and pass them to a function one by one, no, that can't exist. Not really. It certainly is not a good idea. For this to work properly, you need to decide at runtime how many parameters to pass the function, a complete circumvention of the type system and all its benefits. You could say, lists are exactly here to properly state "I don't know how many elements there will be. printf
itself rather violates much of Haskell's philosophy. The reason for its typeclass hackery of a variadic signature is not so much to allow a varying number of arguments, but arguments of different type, which is not relevant in your case.
What is easily implemented though is taking a fixed number of elements from the lists. Again not really a great idea because it's necessarily a partial function... but here goes:
pop3 :: (a -> a -> a -> b) -> [a] -> b
pop3 f [x,y,z] = f x y z
allowing you to write
printf "(%s) %s-%s" `pop3` splitPlaces [3, 3, 4] phoneNumber
Actually, we can generalise this: still fixed number at compile-time, but with a single name for any such number:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
class YieldsEventually f a b where
popN :: f -> [a] -> b
instance YieldsEventually b a b where
popN = const
instance (YieldsEventually f a b) => YieldsEventually (a->f) a b where
popN f (x:xs) = popN (f x) xs
However, combining two functions that both require monomorphic arguments to resolve their own polymorphism-variadicity can't possibly work well, you need to fully qualify everything to make it compile:
(printf "(%s) %s-%s" :: String -> String -> String -> IO())
`popN` (splitPlaces [3, 3, 4] phoneNumber :: [String])
Not nice.
I think your original approach is the best; the explicit pattern also allows you to insert proper failure handling.
Finally, and obviously evilest, here is the dynamic-numbers-of-arguments solution:
{-# LANGUAGE RankNTypes #-}
popNPrintfr :: PrintfArg a => (forall p. PrintfType p => p) -> [a] -> IO ()
popNPrintfr printr [] = printr
popNPrintfr printr (x:xs) = popNPrintfr (printr x) xs
Again simpler to be used:
printf "(%s) %s-%s" `popNPrintfr` splitPlaces [3, 3, 4] phoneNumber