No history yet

Haskell Foundations

Transcript

Beau

Alright, Jo. I've spent years deep in the guts of Node.js, wrestling with the event loop, managing state in Python... I feel like I have a good handle on how computers *work*. But every time I look at Haskell, it feels like reading a different language. Not just a programming language, like... a different *human* language.

Jo

That's because you're trained to think in terms of instructions. 'Do this, then do that, then if this is true, change this variable.' Right? Python and Node are imperative. They're a sequence of commands.

Beau

Yeah, a recipe. A set of steps for the machine to follow. How else would it work?

Jo

Haskell isn't a recipe. It's a set of definitions. You don't tell it *how* to do something, you tell it what something *is*. The fundamental shift is that in Haskell, everything is an expression that evaluates to a value. There are no 'statements' in the C-family sense.

Beau

Okay, 'everything is an expression.' I've heard that phrase. Unpack that for me. Like, an if-then-else is an expression?

Jo

Exactly. In Python, you'd write 'if condition: x = 5 else: x = 10'. The 'if' is a statement that directs traffic. In Haskell, you'd write 'let x = if condition then 5 else 10'. The entire if-then-else block resolves to a single value, 5 or 10, which then gets bound to x. It has to have an else. It can't evaluate to... nothing.

Beau

Ah, okay. So it's more like a ternary operator in JavaScript or Python, but for everything.

Jo

That's a great way to think about it. This leads to the core concept of purity. Since functions are just expressions that map inputs to outputs, they behave like true mathematical functions. You know, from your linear algebra background. A matrix transformation takes a vector and produces a new vector. It doesn't... you know, log the result to a cosmic console or change the value of pi.

Beau

Right, no side effects. So a Haskell function `add(2, 3)` will always, under all circumstances, return 5. It can't secretly increment a global counter or write to a database. That's referential transparency, right? You can replace the function call with its resulting value without changing the program's behavior.

Jo

Precisely. And that sounds simple, but it has massive implications. For one, it makes reasoning about code incredibly simple. But it leads to a very practical question you're probably already thinking of.

Beau

Yeah... how do you *do* anything? How do you print 'Hello, world!' if printing to the console is a side effect? How do you read a file?

Jo

We'll get to how Haskell elegantly quarantines those side effects later, using things like Monads. But before that, there's another 'weird' piece of the puzzle. Lazy evaluation.

Beau

Okay. So, in Python, if I write `x = some_heavy_computation()`, it stops everything and runs that function right then and there. What does Haskell do?

Jo

Haskell says, 'Okay, I see that x is defined by this computation. Got it.' and moves on. It doesn't actually *run* the computation. It just creates a promise, a 'thunk,' which is basically the recipe for how to compute x when it's needed.

Beau

So... when is it needed?

Jo

When something else, like a function that prints to the screen, actually needs the value of x. At that moment, and only then, the runtime system looks at the thunk, executes the computation, and replaces the thunk with the actual result. If you ask for x again, it's already there, no re-computation needed.

Beau

Whoa. Okay, so if I define a variable but never use it, its computation never even runs?

Jo

Never. This allows for some mind-bending things, like defining infinite lists. You can define a list of all positive integers, `[1..]`. It's not stored in memory. It's a thunk that knows how to generate the next number when you ask for it. Then you can say 'give me the first ten numbers from that infinite list,' and it'll compute just those ten.

Beau

That's... really different. It feels less like a call stack and more like a dependency graph that gets resolved on demand.

Jo

That's the perfect analogy. Call-by-need. It changes how you structure programs. So let's connect this to functions. In Haskell, functions are first-class citizens. You can pass them as arguments, return them from other functions... all the stuff you see with higher-order functions in Python or JavaScript.

Beau

Like Python's `map` or `filter`.

Jo

Exactly. But Haskell takes it a step further with currying. Let's say you have a function `add x y`. You might think it takes two arguments. But in Haskell, every function officially only takes one argument.

Beau

Wait, what? How does `add` work then?

Jo

When you call `add 5`, it doesn't fail. It returns a *new function*. A function that takes one argument, `y`, and adds 5 to it. You've partially applied the function.

Beau

Ah, so I could do `let addFive = add 5`, and then later call `addFive 10` to get 15. That's actually really powerful. You're creating specialized functions on the fly.

Jo

You got it. And this is the bread and butter of functional programming. You build complex behaviors by composing simple, pure functions together, often by partially applying them. You could define a `greaterThan` function, partially apply it with 10 to get an `isGreaterThanTen` function, and then pass *that* to a filter.

Beau

Okay, that's clicking. It's a very compositional way of thinking. So, if I want to get my hands dirty, where do I start? What's the equivalent of `node` or `python` in a terminal?

Jo

You're looking for GHCi, the Glasgow Haskell Compiler's interactive environment. You just type `ghci` in your terminal. It's a REPL, just like you're used to. You can define functions, test expressions, and load modules right there. A typical workflow is writing your functions in a `.hs` file, which is a module, and then loading it into GHCi with `:l YourModule.hs` to play with it.

Beau

So the mental model is: define a universe of pure, lazy, composable functions in modules, then load them up and ask the interactive environment to evaluate an expression that uses them.

Jo

That's the perfect summary. You're not writing a script of actions; you're building a system of mathematical truths and then asking for one of them to be resolved.