In http://www.haskell.org/pipermail/haskell-cafe/2007-August/030096.html the typeclass method collide
is defined as taking a 2-tuple as its single argument, rather than two "normal" arguments (I think I understand partial application, etc.).
{-# 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"
What is the purpose of this?
When is it better to use a tuple argument rather than multiple arguments?
Haskell tuple is the structure by which we can manage to store or group the values inside it. The tuple in Haskell is a fixed sized structure which can hold any type of data type inside it.
A tuple is a sequence of values. The values can be of any type, and they are indexed by an integer, so tuples are not like lists. Tuples are immutable which means you cannot add more elements to the tuple as the program runs.
Use parentheses and commas to create tuples. Use one comma to create a pair. Use more commas to create tuples with more components. Note that it is also possible to declare tuples using in their unsugared form.
I almost never write functions that take tuples as arguments. If the situation arises where variables are inherently connected (as bheklilr mentioned in a comment), I'm more likely to box that up into it's own separate data type and pattern match on it.
One common situation where you might want to define a function that takes tuples as arguments is when you have a list (or any arbitrary Functor
) of tuples that you generate on the fly, but want to map over it with some function, e.g.
grid :: [(Int, Int)]
grid = (,) <$> [1..10] <*> [1..10]
You might want to, say, add the first and second values of all of the tuples in your grid (for whatever reason), which you could do by mapping a tuple-consuming function over grid
:
addTuple :: (Int, Int) -> Int
addTuple (x, y) = x + y
sumPoints :: [(Int, Int)] -> [Int]
sumPoints = map addTuple
What I would rather do in this situation is just use uncurry
(:: (a -> b -> c) -> (a, b) -> c
) to use +
just like normal:
sumPoints :: [(Int, Int)] -> [Int]
sumPoints = map (uncurry (+))
This is arguably clearer and definitely shorter; it's also extremely easy to define higher-order analogues such as uncurry3
, for example:
> let uncurry3 f (a, b, c) = f a b c
> uncurry3 (\a b c -> a + b + c) (1, 2, 3)
6
I would say, normally, function should be curried (so no tuples) except is the arguments themself are tuples. For example If you write a function to add two numbers, you have 2 arguments, so you should write it :
add :: Num a => a -> a -> a
add x y = x + y
Now, if for some reason your are using 2-uple as 2-D point, and you want to add two points. That's till two arguments, which appears to be tuples so you would write like that
add :: Num a => (a,a) -> (a,a) -> (a,a)
add (x,y) (x,y') = (x+x', y+y')
Writing
add :: Num a => a -> a -> a -> a -> (a,a)
add a b c d = (a+c, b+d)
Wouldn't really make sense because the entity your dealing with are tuples. You wouldn't write that way either
add :: Num a => ((a,a),(a,a)) -> (a,a)
In our example, it's probable that the collide
function is always called in a context when thing to check are served as a tuple (because maybe there is stage which collect all possible collision resulting in a list of 2-uples). In that situation it might be easier then to have a uncurried function, so you can map collide
over it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With