在http://www.haskell.org/pipermail/haskell-cafe/2007-August/030096.html中,类型类方法collide
被定义为将2元组作为其单个参数,而不是两个“正常”参数(我认为我理解部分应用等)。 / p>
{-# OPTIONS_GHC -fglasgow-exts
-fallow-undecidable-instances
-fallow-overlapping-instances #-}
module Collide where
class Collide a b where
collide :: (a,b) -> String
data Solid = Solid
data Asteroid = Asteroid
data Planet = Planet
data Jupiter = Jupiter
data Earth = Earth
instance Collide Asteroid Planet where
collide (Asteroid, Planet) = "an asteroid hit a planet"
instance Collide Asteroid Earth where
collide (Asteroid, Earth) = "the end of the dinos"
-- Needs overlapping and undecidable instances
instance Collide a b => Collide b a where
collide (a,b) = collide (b, a)
-- ghci output
*Collide> collide (Asteroid, Earth)
"the end of the dinos"
*Collide> collide (Earth, Asteroid)
"the end of the dinos"
这是为了什么目的?
何时使用元组参数而不是多个参数会更好?
答案 0 :(得分:4)
我几乎从不编写将元组作为参数的函数。如果出现变量固有连接的情况(如评论中提到的bheklilr),我更有可能将其包含在其自身的独立数据类型和模式匹配中。
您可能希望定义一个以元组作为参数的函数的一种常见情况是,您有一个动态生成的列表(或任意Functor
)元组,但希望用一些功能映射它,例如
grid :: [(Int, Int)]
grid = (,) <$> [1..10] <*> [1..10]
您可能希望添加网格中所有元组的第一个和第二个值(无论出于何种原因),您可以通过将一个消耗元组的函数映射到grid
来实现:
addTuple :: (Int, Int) -> Int
addTuple (x, y) = x + y
sumPoints :: [(Int, Int)] -> [Int]
sumPoints = map addTuple
在这种情况下我宁愿做的只是使用uncurry
(:: (a -> b -> c) -> (a, b) -> c
)来正常使用+
:
sumPoints :: [(Int, Int)] -> [Int]
sumPoints = map (uncurry (+))
这可以说更清晰,也更短;它也非常容易定义高阶类似物,例如uncurry3
,例如:
> let uncurry3 f (a, b, c) = f a b c
> uncurry3 (\a b c -> a + b + c) (1, 2, 3)
6
答案 1 :(得分:2)
我会说,通常,函数应该是curry(所以没有元组),除了参数本身是元组。例如,如果你编写一个函数来添加两个数字,你有2个参数,所以你应该写它:
add :: Num a => a -> a -> a
add x y = x + y
现在,如果由于某种原因你使用2-uple作为2-D点,并且你想要添加两个点。直到两个参数,似乎是元组,所以你会像那样写
add :: Num a => (a,a) -> (a,a) -> (a,a)
add (x,y) (x,y') = (x+x', y+y')
写作
add :: Num a => a -> a -> a -> a -> (a,a)
add a b c d = (a+c, b+d)
真的没有意义,因为你处理的实体是元组。 你不会这样写
add :: Num a => ((a,a),(a,a)) -> (a,a)
在我们的示例中,当要检查的事物服务作为元组时,可能总是在上下文中调用collide
函数(因为可能存在收集所有可能的碰撞的阶段得到一个2-uples列表)。在那种情况下,可能更容易拥有一个未计算的函数,因此您可以将collide
映射到它上面。