Reading Real World Haskell and the Typeclassopedia I get the impression that 2-tuples (a,b)
can have very special roles in Haskell.
The first use that I came across is with lookup
where we use a list of 2-tuples as a dictionary.
Then I also came across the fact that ((,) e)
is an instance of a functor (but no other n-tuple), which makes sense for example in the above example of (key,value)
.
Now the most recent case - which is the one I actually want to ask about - is in chapter 4.3 of the Typeclassopedia. There it says that ((,) a)
is an instance of Applicative
if a
is a monoid. When do you actually make use of that? What are applications where you use the Applicative
instance of (a,b)
?
We can do that by using a tuple containing two or more elements. A tuple is an ordered set of one or more elements, which can have different types. The number of elements in a particular tuple is fixed when that tuple is created.
Concatenating and Multiplying TuplesConcatenation is done with the + operator, and multiplication is done with the * operator. Because the + operator can concatenate, it can be used to combine tuples to form a new tuple, though it cannot modify an existing tuple. The * operator can be used to multiply tuples.
Tuple. Tuples are used to store multiple items in a single variable. Tuple is one of 4 built-in data types in Python used to store collections of data, the other 3 are List, Set, and Dictionary, all with different qualities and usage. A tuple is a collection which is ordered and unchangeable.
31.2 Python Collection Types Tuples allow duplicate members and are indexed. Lists Lists hold a collection of objects that are ordered and mutable (changeable), they are indexed and allow duplicate members. Sets Sets are a collection that is unordered and unindexed.
There's nothing preventing us from writing an instance for triples or arbitrary n-tuples:
instance Functor ((,,) a b) where
fmap f (x,y,z) = (x,y,f z)
instance (Monoid a, Monoid b) => Applicative ((,,) a b) where
pure z = (mempty, mempty, z)
(a,b,f) <*> (x,y,z) = (a `mappend` x, b `mappend` y, f z)
Pairs aren't special therefore in the sense that you can implement the instance for any n-tuple. But since those instances have to be written somewhere, it begs the question how far we should go. For example, Monoid
instances are defined up to 5-tuples. It's certainly possible to write them for 10-tuples, but we're just copying boiler-plate code at that point.
That being said, pairs are special since their collection provide a natural way to describe a relation. One example is a dictionary, which relates a term and its definition:
dictionary :: [(String, String)]
dictionary =
[("cat", "animal that likes strings; not Strings, though")
,("dog", "animal that likes you; yes you")
,("foo", "a strange word used by programmers in examples")
]
The other way to show that relation (if the first parts of all pairs would be unique) would be
partialDictionaryEntry :: String -> String
if we limit the input domain to "cat"
, "dog"
and "foo"
, or
dictionaryEntry :: String -> Maybe String
which is exactly what \s -> lookup s dictionary
would be. And with pairs you can model any other n-tuple:
(a,b,z) = ((a,b),z)
(a,b,c,z) = ((a,b,c),z) = (((a,b),c),z)
In that sense they are the smallest container that provides this functionality. We can build all other tuples types from pairs. Heck, theoretically we don't even need to write our Applicative ((,,) a b)
instance, since it's already provided for (,) (a,b)
due to the Monoid
instance.
That being said, why does it even have a Applicative
instance? It's the simplest Writer
implementation:
log :: (Show a) => a -> (String, a)
log x = (show x ++ "\n", x)
fivePlusThree = (+) <$> log 3 <*> log 5
main = do
let (logs, result) = fivePlusThree
putStrLn logs
print result
3
5
8
This provides an easy way to add additional information to functions or values, although you will probably use Writer
and its writer
method, since they are more pleasant to use and provide a strict variant.
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