This will be a rather short and focused post. Chapter 3 of the book doesn't spring any exciting new concepts on us or cover large topics such as general syntax. Instead it chooses to zoom in on the
String datatype. We look at how they are represented in Haskell and how we can treat them like lists because, spoiler alert: that's what they are.
Like in all compiled languages that I've used, there's a difference between the data types
String and how you represent the literals. As is the convention, a
Char is represented using single quotes (~'a'~), while a string uses double quotes: ~"hey, tiger"~.
However, unlike in a most languages I'm aware of, a
String is actually just syntactic sugar for a linked list of characters (
['h', 'e', 'l', 'l', 'o'] and ~"hello"~ are just two ways of writing the exact same thing. This has some interesting effects on how we handle strings, in that it means anything we can do with a list, we can do with a string, but it also means that certain operations will be very costly. There are alternatives for when you need performance, but let's not concern ourselves with that for now.
In the introduction to handling strings (secretly lists), we are introduced to the functions
drop, and the operator
!!, so let's have a quick look at what they do.
tail functions return the first element of the list and the list without the first element respectively; so that if you take the
head of a list, the
tail of a list, and then put them back together, you'd have the original list back:
splitAndPutBackTogether list = let h = head list t = tail list in h : t
Side note: this is the first time we're seeing that ~:~ operator. It's known as ~cons~ and it adds a value to the front of a list.
Be aware that these functions---~head~ and
tail---are not total, and will throw exceptions at you if you give them an empty list.
A *total function* is one that is well defined and gives you a result for every input value---i.e. maps every value from the /domain/ into a value in the /image/ (the subset of the /codomain/ containing possible output values), to use the terminology from chapter one. Functions that are defined only for a subset of their valid arguments are known as *partial functions*. More on this later on in the book.
drop functions, though, are total, and will not throw anything in your face. They operate on lists, take an
Int as the first argument and then split the list after as many elements as specified by the
Int, giving you either the first portion or the second. They're pretty much like
tail, except you can choose how many elements go in each pile, and you'll always get a list back. If there aren't enough elements in the list to reach the specified number,
take will give you back the whole list, and
drop will give you an empty list.
!! is the index operator and returns the element at the specified index in the list. The equivalent of
[n] in a lot of other languages. Zero-indexed and unsafe: ~"hey" !! 2~ would return ~'y'~, while ~"hey" !! 5~ would crash.
There are only a few interesting definitions in this chapter:
- a sequence of characters. In Haskell, the
Stringtype is just an alias for a linked list of characters,
- Type (or datatype)
- "A classification of values or data", such as
String, etc. Notably does not have any behavior associated with it.
- Joining sequences of values together. Often used with lists, it's the act of putting one before the other and creating a new list. The operator
(++)takes care of it in Haskell.
That's it for this chapter. Next time, we'll be looking at basic datatypes. This is when things start to get interesting, so strap yourself in and get ready.