我有以下代码可以格式化电话号码.(printf
来自Text.Printf,splitPlaces
来自Data.List.Split).
prettyPrint :: String -> String prettyPrint phoneNumber = printf "(%s) %s-%s" part1 part2 part3 where [part1, part2, part3] = splitPlaces [3, 3, 4] phoneNumber
我正在寻找的是一个允许以下方式编写函数的运算符:
prettyPrint = printf "(%s) %s-%s"splitPlaces [3, 3, 4]
这样的运营商是否存在?它可以存在吗?
假设此运算符应弹出列表中的元素并将它们逐个传递给函数,否则不能存在.不是真的.这当然不是一个好主意.为了使其正常工作,您需要在运行时决定传递函数的参数数量,完全规避类型系统及其所有好处.你可以说,列表正好在这里正确地陈述"我不知道会有多少元素.printf
本身相当违反Haskell的哲学.它的类型类hackery的可变参数签名的原因并不是允许变化参数的数量,但不同类型的参数,在您的情况下不相关.
虽然容易实现的是从列表中获取固定数量的元素.再一次不是一个好主意,因为它必然是一个部分功能...但是这里有:
pop3 :: (a -> a -> a -> b) -> [a] -> b pop3 f [x,y,z] = f x y z
允许你写
printf "(%s) %s-%s" `pop3` splitPlaces [3, 3, 4] phoneNumber
实际上,我们可以概括一下:在编译时仍然是固定数字,但是对于任何这样的数字都有一个名称:
{-# 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
但是,结合两个需要单态参数来解决自己的多态性 - 变量的函数不可能很好地工作,你需要完全限定一切以使其编译:
(printf "(%s) %s-%s" :: String -> String -> String -> IO()) `popN` (splitPlaces [3, 3, 4] phoneNumber :: [String])
不好.
我认为你原来的方法是最好的; 显式模式还允许您插入适当的故障处理.
最后,显而易见的是,这里是动态数论证解决方案:
{-# LANGUAGE RankNTypes #-} popNPrintfr :: PrintfArg a => (forall p. PrintfType p => p) -> [a] -> IO () popNPrintfr printr [] = printr popNPrintfr printr (x:xs) = popNPrintfr (printr x) xs
再次使用起来更简单:
printf "(%s) %s-%s" `popNPrintfr` splitPlaces [3, 3, 4] phoneNumber