实际上,您只需要Functor
:
listUncurry :: Functor f => (a -> a -> a -> r) -> f [a] -> f r
listUncurry h p =
(\[x, y, z] -> h x y z) <$> p
Functor
对我来说,只有当您有如下代码模式时才需要提示:
do x <- m
return (f ...)
这相当于
m >>= (\x -> return (f ...))
这与
fmap (\x -> f ...) m
这是因为单子定律暗示了这种身份:
fmap f xs = xs >>= return . f
多元变量listUncurry
在大多数情况下,我并不真正推荐这样做,因为它会将编译时错误转换为运行时错误,但这是实现多变量的方式listUncurry
:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
class ListUncurry a x r where
listUncurry :: a -> [x] -> r
instance ListUncurry k a r => ListUncurry (a -> k) a r where
listUncurry f (x:xs) = listUncurry (f x) xs
listUncurry _ _ = error "listUncurry: Too few arguments given"
instance ListUncurry r a r where
listUncurry r [] = r
listUncurry _ _ = error "listUncurry: Too many arguments given"
如果您也使用它,您将需要大量显式类型注释。可能有一种方法可以使用类型族或函数依赖来帮助解决这个问题,但我现在想不出它。由于这可能是可以解决的(至少在一定程度上),在我看来,更大的问题是类型错误从编译时错误变为运行时错误。
示例用法:
ghci> listUncurry ord ['a'] :: Int
97
ghci> listUncurry ((==) :: Int -> Int -> Bool) [1,5::Int] :: Bool
False
ghci> listUncurry ((==) :: Char -> Char -> Bool) ['a'] :: Bool
*** Exception: listUncurry: Too few arguments given
ghci> listUncurry ((==) :: Char -> Char -> Bool) ['a','b','c'] :: Bool
*** Exception: listUncurry: Too many arguments given
更安全的listUncurry
如果您将课程更改为
class ListUncurry a x r where
listUncurry :: a -> [x] -> Maybe r
并适当地更改实例中的错误情况,您至少会获得一个更好的界面来处理错误。如果您想保留该信息,您还可以将 替换为Maybe
区分“太多”和“太少”参数错误的类型。
我觉得这会是一种更好的方法,尽管您需要添加更多的错误处理(Maybe
尽管如此Functor
,Applicative
但Monad
接口会变得相当好)。
比较两种方法
这最终取决于这将代表什么样的错误。如果程序执行在遇到此类错误时无法以任何有意义的方式继续执行,那么第一种方法(或类似的方法)可能比第二种方法更合适。如果有任何方法可以从错误中恢复,则第二种方法会比第一种方法更好。
是否应该首先使用多变量技术是一个不同的问题。重组程序以避免多变量内容的额外复杂性可能会更好。