Lately I’ve been thinking about what to do to better understand compilers and interpreters, having done most everything so far by the seat of my pants. Fun, but not necessarily recommended. Some possibilities:

 Robert Nystrom’s Crafting Interpreters
 Stephen Diehl’s Write You a Haskell
 William Appel’s Modern Compiler Implementation in ML
I looked into the Nystrom’s book, and liked the first chapters, but the code, in Java, puts me off. I also looked into (3), but that would require learning ML. I tried this briefly, and it was pleasant. Most suited to my tastes was Stephen Diehl’s online book (2). There is, for example, this quote:
In mathematics a function is defined as a correspondence that assigns exactly one element of a set to each element in another set. If a function f(x)=a then the function evaluated at x will always have the value a. Central to the notion of all mathematics is the notion of equational reasoning, where if a=f(x) then for an expression g(f(x),f(x)), this is always equivalent to g(a,a). In other words, the values computed by functions can always be substituted freely at all occurrences.
The implementation may perform effects, but central to this definition is the unobservability of such effects. A function is said to be referentially transparent if replacing a function with its computed value output yields the same observable behavior.
The choice of (2) requires more Haskell than I am comfortable with, but my experience with Elm, another statically typed language of pure functions should help. I’m going to take the plunge!