Stringand returns a
String. Never mind the implementation, it's the type signature we're interested in.
a -> bwhere
bare variables for any type. So the signatures for
capitalizecan be read as "a function from
String". In other words, it takes a
Stringas its input and returns a
Stringas its output.
strLengthis the same idea as before: we take a
Stringand return you a
matchyou can interpret as: It takes a
Stringand returns you
[String]. But an interesting thing is going on here that I'd like to take a moment to explain if I may.
matchwe are free to group the signature like so:
Regexand returns us a function from
[String]. Because of currying, this is indeed the case: give it a
Regexand we get a function back waiting for its
Stringargument. Of course, we don't have to think of it this way, but it is good to understand why the last type is the one returned.
matchthat already has a
replace, the extra notation can get a little noisy and redundant so we simply omit them. We can give all the arguments at once if we choose so it's easier to just think of it as:
Stringand returns you a
idfunction takes any old type
aand returns something of the same type
a. We're able to use variables in types just like in code. Variable names like
bare convention, but they are arbitrary and can be replaced with whatever name you'd like. If they are the same variable, they have to be the same type. That's an important rule so let's reiterate:
a -> bcan be any type
ato any type
a -> ameans it has to be the same type. For example,
String -> Stringor
Number -> Number, but not
String -> Bool.
mapsimilarly uses type variables, but this time we introduce
bwhich may or may not be the same type as
a. We can read it as:
maptakes a function from any type
ato the same or different type
b, then takes an array of
a's and results in an array of
b, an array of
a, and it delivers us an array of
b. The only sensible thing for it to do is call the bloody function on each
a. Anything else would be a bold face lie.
reduceis perhaps, the most expressive of all. It's a tricky one, however, so don't feel inadequate should you struggle with it. For the curious, I'll try to explain in English though working through the signature on your own is much more instructive.
a, and produces a
b. Where might it get these
bs? Well, the following arguments in the signature are a
band an array of
as so we can only assume that the
band each of those
as will be fed in. We also see that the result of the function is a
bso the thinking here is our final incantation of the passed in function will be our output value. Knowing what reduce does, we can state that the above investigation is accurate.
head, we see that it takes
a. Besides the concrete type
array, it has no other information available and, therefore, its functionality is limited to working on the array alone. What could it possibly do with the variable
aif it knows nothing about it? In other words,
asays it cannot be a specific type, which means it can be any type, which leaves us with a function that must work uniformly for every conceivable type. This is what parametricity is all about. Guessing at the implementation, the only reasonable assumptions are that it takes the first, last, or a random element from that array. The name
headshould tip us off.
reversepossibly be up to? Again, it cannot do anything specific to
a. It cannot change
ato a different type or we'd introduce a
b. Can it sort? Well, no, it wouldn't have enough information to sort every possible type. Can it re-arrange? Yes, I suppose it can do that, but it has to do so in exactly the same predictable way. Another possibility is that it may decide to remove or duplicate an element. In any case, the point is, the possible behaviour is massively narrowed by its polymorphic type.
headof our array, then run some function
fon it, that is equivalent to, and incidentally, much faster than, if we first
map(f)over every element then take the
headof the result.
filtertheorem is similar. It says that if we compose
pto check which should be filtered, then actually apply the
filterwill not transform the elements - its signature enforces that
awill not be touched), it will always be equivalent to mapping our
fthen filtering the result with the
composefunction itself. The fruit is low hanging and the possibilities are endless.
amust be an
Ord. Or in other words,
amust implement the
Ordinterface. What is
Ordand where did it come from? In a typed language it would be a defined interface that says we can order the values. This not only tells us more about the
aand what our
sortfunction is up to, but also restricts the domain. We call these interface declarations type constraints.
Show. Those will ensure that we can check equality of our
as and print the difference if they are not equal.