reservadas - peta haskell en español
¿Hay una mejor manera de tener argumentos opcionales en Haskell? (5)
Estoy acostumbrado a poder definir argumentos opcionales como ese en Python:
def product(a, b=2):
return a * b
Haskell no tiene argumentos predeterminados, pero pude obtener algo similar mediante el uso de Maybe:
product a (Just b) = a * b
product a Nothing = a * 2
Sin embargo, esto se vuelve engorroso muy rápidamente si tiene más de múltiples parámetros. Por ejemplo, ¿qué sucede si quiero hacer algo como esto?
def multiProduct (a, b=10, c=20, d=30):
return a * b * c * d
Tendría que tener ocho definiciones de multiProduct para dar cuenta de todos los casos.
En cambio, decidí ir con esto:
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
Eso se ve muy poco elegante para mí. ¿Hay una forma idiomática de hacer esto en Haskell que sea más limpia?
Aquí hay una expresión idiomática de Neil Mitchell , que también parece respaldada por Brent Yorgey .
Aquí hay otra forma de hacer argumentos opcionales en Haskell:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
module Optional where
class Optional1 a b r where
opt1 :: (a -> b) -> a -> r
instance Optional1 a b b where
opt1 = id
instance Optional1 a b (a -> b) where
opt1 = const
class Optional2 a b c r where
opt2 :: (a -> b -> c) -> a -> b -> r
instance Optional2 a b c c where
opt2 = id
instance (Optional1 b c r) => Optional2 a b c (a -> r) where
opt2 f _ b = /a -> opt1 (f a) b
{- Optional3, Optional4, etc defined similarly -}
Entonces
{-# 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"
Actualización : Vaya, me aceptaron. Honestamente creo que la respuesta de luqui es la mejor aquí:
- el tipo es claro y fácil de leer, incluso para principiantes
- lo mismo para los errores de tipo
- GHC no necesita sugerencias para hacer una inferencia de tipo con él (pruebe
opt2 replicate 3 ''f''
en ghci para ver lo que quiero decir) - los argumentos opcionales son independientes de la orden
Cuando los argumentos se vuelven demasiado complejos, una solución es crear un tipo de datos solo para los argumentos. Luego puede crear un constructor predeterminado para ese tipo y completar solo lo que desea reemplazar en sus llamadas a funciones.
Ejemplo:
$ 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
}
No conozco una mejor manera de resolver el problema subyacente, pero su ejemplo puede escribirse de manera más sucinta como:
multiProduct req1 opt1 opt2 opt3 = req1 * opt1'' * opt2'' * opt3''
where opt1'' = fromMaybe 10 opt1
opt2'' = fromMaybe 20 opt2
opt3'' = fromMaybe 30 opt3
Tal vez una buena notación sería más fácil para los ojos:
(//) :: 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)
Si necesita usar los parámetros más de una vez, sugiero ir con el método de @ pat.
EDIT 6 años después
Con ViewPatterns
puede poner los valores predeterminados a la izquierda.
{-# 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