Is the abstract stuff at the top or the bottom?
This is an episode of Thoughts on Functional Programming, a podcast by Eric Normand.
I explore a new perspective about what abstraction means and how it can cause problems.
[00:00:00] Is the more abstract stuff at the top of the stack or at the bottom of the stack? Hello, my name is Eric Normand and this is my podcast. Welcome. Today, I want to talk about this idea that I've been exploring. I believe that in a layered architecture or a stratified design, the more general stuff is further down, and the more specific stuff is further up.
[00:00:36] Another way to put it: You could say the stuff at the bottom is more abstract, and the stuff at the top is more concrete. I just translated general or generic to abstract and specific to concrete.
[00:01:18] And as you start programming your application, you build more specific things in it, things that perhaps are only useful for your business or for businesses of your type— so you might start building stuff that's good for accounting that might not be good for physics applications— and then you also build on top of that specific things that might only be useful for your business, like the way you calculate a discount.
[00:01:52] And then also you're building very specific stuff like the particular GUI that you want to show to your users. That's very specific to your particular application.
[00:02:58] So to me, this seems pretty clear, but I was listening to someone talk about how you are building up abstractions when you're programming. And you're forgetting about the concrete thing that's actually running.
[00:03:19] This person went on and they explained what they meant, and he made a good point. There is some concrete work that you need the computer to do. And all the stuff on top that you build is, at most, only useful for you to help you organize your code or to help someone read it. But really what you need is for this concrete work to get done, like adding all these numbers together and sending that over the wire to wherever it needs to go. And that concrete work, doesn't really care about all the abstract stuff that your language gives you, like what class the thing is defined in and what methods got called and all that.
[00:04:09] The work that needed to be done was it needed to call the add instruction of your machine code so many times to finally get the answer that it needed.
[00:04:21] Okay. Notice that this is actually the opposite direction. It's a very common view.
[00:04:28] The abstract stuff is on the top. And the concrete stuff is on the bottom.
[00:04:35] So exactly opposite of mine. I've been thinking about it, like did these two contradict. Obviously they do because they can't be both right at the same time.
[00:04:48] But I'm also a fan of having multiple perspectives. And multiple perspectives help you by giving you different ways to understand a problem. Each of those ways might reveal a new thing that will help you solve a problem.
[00:05:10] We're not able to do general purpose problem solving. We actually need to pick a particular perspective, and address a problem using that perspective. There is no universal reference frame. And so we always have to choose a perspective. So I like to have a quiver of different perspectives to use and apply them all. Of course, it's hard to do them all at the same time. You got to do one at a time.
[00:05:40] So I've just been thinking a lot about how these two relate. I've already gone over the one that I use by default, I guess you'd say. But I've also been thinking a lot about this opposite one where the concrete stuff is at the bottom. And what it would mean what it implies.
[00:06:05] Really, if you continue this spectrum all the way to the bottom, the most concrete work is the electrons flowing through this circuit. That would mean we need the electrons to flow through the circuit in a certain way. And that is the work that we would like done. Now, unfortunately, it's very hard for us to imagine our work directly in terms of electrons flowing through a super complex circuit, like a computer chip.
[00:06:43] So we need something to help us make the work happen. One way to do that is you have a machine code and then you have assembly and you just build these more abstract things on top.
[00:06:58] But sometimes, at a certain point, the abstractions become an impediment to making the work that you want to happen happen. This is often the case when you're trying to optimize something.
[00:07:13] You are working through various levels of indirection. For instance, your programming language might run on a virtual machine. And the language gets translated into the virtual machine's byte code. And then that byte code gets translated, somehow interpreted or compiled down to the machine code.
[00:07:41] And it becomes hard to control what is running, when.
[00:07:47] What is the actual work that's happening on the machine? Which again, translates into down below more concrete. What electrons are flowing through which wires in your circuit.
[00:08:00] And you need that control because perhaps it's too slow.
[00:08:04] Or it's using more memory than you want. And you need the control to be able to make it use less memory.
[00:08:12] So that's the view. You're adding more and more abstraction on top of the work that needs doing.
[00:08:19] This view is very common in areas where you do need lots of control and usually for performance. An operating system developer would be thinking about this stuff. And a, a game programmer would be thinking about this, some kind of embedded programmer with very limited memory and a low power CPU. They would be thinking about it too.
[00:08:47] Now another trick you can do is to look at one perspective through the lens of the original perspective. If I was talking to someone who had this perspective, let's say they're an operating system developer. What would someone who has the opposite view say about them?
[00:09:09] The problem is in the types of abstractions they are using.
[00:09:15] Abstractions are not meant to obscure or add unnecessary levels of indirection. They are meant to clarify and make something more precise.
[00:09:30] It is true that we can, accidentally, or through unskillful use of indirection, make something more difficult to control. And we do that all the time. The library will claim that it makes something easier and it probably does. But then it doesn't give you the direct control that you might need in a particular situation. It was made for a more general situation or some other adjacent situation and it doesn't quite give you the control you need for the thing you're doing.
[00:10:05] That doesn't mean that abstraction is bad. It's just that at some point, you are using the wrong abstraction or a bad abstraction. Meaning badly coded.
[00:10:16] That perspective of what is the actual work that needs doing? Making that concrete. That perspective is very valuable. If you do have good abstractions, perhaps instead of throwing out the abstractions and dealing directly. So that would mean something like, well, I'm going to throw out my high level language and code this in assembly, because I need the control. Perhaps, instead of doing that, you would be better off working in the compiler of whatever your higher level language is.
[00:10:52] And in that way, you're thinking in your high level language which is in this particular perspective, more abstract. Further from the metal. But you're able to introduce the optimizations into the compiler, so that it's still fast and you still have that control.
[00:11:15] So instead of just throwing it away and writing it directly yourself, you are still gaining the leverage that your high level language and compiler and give you.
[00:11:27] I also feel like I have already done the opposite of that, which is looking at my original perspective through the lens of this new concrete at the bottom perspective.
[00:11:42] That it was kind of already a critique. That when you're building up functions that call functions that call functions, you're actually building opaque and hard to control structures that what you really need is to make sure that the right work is getting done.
[00:11:59] The lesson I think is: you need good abstractions.
[00:12:03] I just want to also bring up one more perspective another meta perspective we can apply, which is hybrid.
[00:12:14] Right now we have this straight line spectrum: concrete to abstract. Either the concrete is at the top and the abstract's at the bottom. Or the abstract is at the top and the concrete is at the bottom. But what if we could point to a particular layer, in our stratified design, and say that is the concrete one. And then up and down from there are both abstract.
[00:12:42] Now that means that you have a choice to make. What layer do you choose? So if you're optimizing your program, you're probably writing it in assembly. So you would peg that you would say, okay, we're not going to go below assembly, because we can't control below that. It's just abstract. Down to the transistors, just super abstract. And everything above it gets more and more abstract as we build up.
[00:13:13] Now, if you're writing in Java, and you've got this stack of classes and methods that call other methods and. You know, frothy at the top. Super abstract. You're deciding I need to optimize this. My concrete layer is the Java language itself. You might also go down a layer to the byte code. And you're going to compile it and look at the bike code and say, what is actually running? How does the compiler understand this code?
[00:13:48] But you don't have control below that. You're not going to rewrite the JVM. So you just leave it there. And everything below that that's abstract. Everything above it also abstract.
[00:14:03] So that's a hybrid. You could pin it anywhere you want.
[00:14:09] That was my exploration of that idea. I hope you learned something from it. I certainly did. And. Well, thank you for being there.
[00:14:20] As always. This has been Eric Normand. Rock on.