是否有更好的方法在Haskell中有可选的参数?
我习惯于能够像在Python中那样定义可选参数:
def product(a, b=2): return a * b
Haskell没有默认参数,但是我可以通过使用Maybe获得类似的结果:
product a (Just b) = a * b product a Nothing = a * 2
如果你有多个参数,这很快就变得很麻烦。 例如,如果我想要做这样的事情:
def multiProduct (a, b=10, c=20, d=30): return a * b * c * d
我将不得不有八个multiProduct的定义来说明所有情况。
相反,我决定这样做:
multiProduct req1 opt1 opt2 opt3 = req1 * opt1' * opt2' * opt3' where opt1' = if isJust opt1 then (fromJust opt1) else 10 where opt2' = if isJust opt2 then (fromJust opt2) else 20 where opt3' = if isJust opt3 then (fromJust opt3) else 30
这对我来说很不雅观。 在Haskell中有一种习惯的方法可以做到这一点吗?
下面是在Haskell中做可选参数的另一种方法:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-} module Optional where class Optional1 abr where opt1 :: (a -> b) -> a -> r instance Optional1 abb where opt1 = id instance Optional1 ab (a -> b) where opt1 = const class Optional2 abcr where opt2 :: (a -> b -> c) -> a -> b -> r instance Optional2 abcc where opt2 = id instance (Optional1 bcr) => Optional2 abc (a -> r) where opt2 f _ b = \a -> opt1 (fa) b {- Optional3, Optional4, etc defined similarly -}
然后
{-# LANGUAGE FlexibleContexts #-} module Main where import Optional foo :: (Optional2 Int Char String r) => r foo = opt2 replicate 3 'f' _5 :: Int _5 = 5 main = do putStrLn $ foo -- prints "fff" putStrLn $ foo _5 -- prints "fffff" putStrLn $ foo _5 'y' -- prints "yyyyy"
更新 :哎呀,我被接受了。 我真的认为卢奎的答案是最好的答案 :
- types清晰,易于阅读,即使是初学者
- types错误也一样
- GHC不需要提示来进行types推断(尝试在ghci中select
opt2 replicate 3 'f'
来查看我的意思) - 可选参数是顺序无关的
也许一些很好的符号会更容易在眼睛上:
(//) :: Maybe a -> a -> a Just x // _ = x Nothing // y = y -- basically fromMaybe, just want to be transparent multiProduct req1 opt1 opt2 opt3 = req1 * (opt1 // 10) * (opt2 // 20) * (opt3 // 30)
如果您不止一次需要使用这些参数,我build议您使用@ pat的方法。
编辑6年后
使用ViewPatterns
你可以把默认值放在左边。
{-# LANGUAGE ViewPatterns #-} import Data.Maybe (fromMaybe) def :: a -> Maybe a -> a def = fromMaybe multiProduct :: Int -> Maybe Int -> Maybe Int -> Maybe Int -> Int multiProduct req1 (def 10 -> opt1) (def 20 -> opt2) (def 30 -> opt3) = req1 * opt1 * opt2 * opt3
我不知道解决潜在问题的更好方法,但是你的例子可以写得更简洁:
multiProduct req1 opt1 opt2 opt3 = req1 * opt1' * opt2' * opt3' where opt1' = fromMaybe 10 opt1 opt2' = fromMaybe 20 opt2 opt3' = fromMaybe 30 opt3
这里有一个来自Neil Mitchell的成语 ,这似乎也得到了Brent Yorgey的赞同 。
当参数变得太复杂的时候,一个解决scheme就是为参数创build一个数据types。 然后你可以为这个types创build一个默认构造函数,并且只填写你想在你的函数调用中replace的内容。
例:
$ runhaskell dog.hs Snoopy (Beagle): Ruff! Snoopy (Beagle): Ruff! Wishbone (Terrier): Ruff! Wishbone (Terrier): Ruff! Wishbone (Terrier): Ruff!
dog.hs:
#!/usr/bin/env runhaskell import Control.Monad (replicateM_) data Dog = Dog { name :: String, breed :: String, barks :: Int } defaultDog :: Dog defaultDog = Dog { name = "Dog", breed = "Beagle", barks = 2 } bark :: Dog -> IO () bark dog = replicateM_ (barks dog) $ putStrLn $ (name dog) ++ " (" ++ (breed dog) ++ "): Ruff!" main :: IO () main = do bark $ defaultDog { name = "Snoopy", barks = 2 } bark $ defaultDog { name = "Wishbone", breed = "Terrier", barks = 3 }