I thought everything were functions in Haskell.
Nope, in Haskell, a value is only a function if its type is x -> y for some types x and y.
Note that all functions in Haskell have exactly one input and one output. We represent functions of multiple inputs using currying e.g.:
x :: A
y :: B
z :: C
f :: A -> B -> C -> D
f x :: B -> C -> D
f x y :: C -> D
f x y z :: D
-- With explicit parentheses:
f :: A -> (B -> (C -> D))
f x :: B -> (C -> D)
(f x) y :: C -> D
((f x) y) z :: D
Casually, we would say this function takes 3 arguments, but this is just another way of thinking about the chain of unary functions; in reality, it can take up to 3 arguments—or even more, if D is a function type!
tom :: Reader String String
what does this mean exactly?
tom is a variable, or a binding, which is bound to a value of type Reader String String. This represents an action in the Reader String context. tom is just like a variable of any other type, including data types like Int or String:
count :: Int
count = length beans
beans :: String
beans = "beans"
Or other “action” types like IO String:
getLength :: IO String
getLength = do
line <- getLine
pure (length line)
What is tom? A function that gets nothing and returns Reader String String?
It’s an action in the Reader String context; it represents a computation which may use a read-only value of type String (the first one) to produce a result of type String (the second).
It’s not a plain function itself, but incidentally a Reader action can be implemented as a newtype which just contains a function:
newtype Reader r a = Reader (r -> a)
runReader :: Reader r a -> r -> a
runReader (Reader f) = f
So runReader tom is a function of type String -> String. Reader allows you to avoid explicitly adding parameters to all of your functions that share some “configuration” or “environment” value (serving one of the roles of “dependency injection” in imperative/OOP languages).
Normally, when a type constructor is an instance of Monad (and therefore also Applicative and Functor), we refer to concrete values of that type as “actions”; some of these values, like [a], Maybe a, and Either e a, are also “containers” of some kind. But others, like Reader r a, State s a, and IO a, are only actions—they don’t really have a “container” interpretation, they’re just descriptions of operations. You can combine these operations in a generic way with >>= / <*> / pure / <$>, but how you “run” an action depends on the particular type.
For Reader and State, there are runReader and runState functions, but for IO, there is no such function. Your Haskell code constructs an IO () value (without side effects!) and the Haskell runtime evaluates this value for you if you assign it to main. This is the sense in which Haskell is “purely functional”.
In other words, an IO String isn’t a String “tagged” with the fact that it came from IO, it’s a procedure for producing a String, which is why you can’t “get the a out of an IO a”.