문제

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
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top