What is the "reify to an interpreter" refactoring? - podcast episode cover

What is the "reify to an interpreter" refactoring?

May 08, 20237 min
--:--
--:--
Listen in podcast apps:

Summary

Eric Normand discusses the "reify to an interpreter" refactoring technique, which involves turning functions into data representations. He explains the steps to mechanically refactor mutation functions into data, using a pizza domain example. This allows for recording user actions and simplifies interpreter creation.

Episode description

Watch the creation of a simple refactoring to turn functions into data.

Transcript

What is the reify to an interpreter refactoring? Hello, my name is Eric Normand and this is my podcast. Welcome. Today I want to talk about... this technique that i'm trying to formalize it's something i've seen done a lot i do it myself But it's kind of informal at the moment. So I'm going to talk through it and try to make it a little bit more rigid, I guess. What I'm calling it is Reified to Interpreter.

So you have all these mutation functions. We talked about those before. They're functions that take the current value and return a modified version of that value. And... These mutation functions are regular functions, but what we want to do is to turn these... functions into data representation of these functions. Normally you would do this when the mutation functions represent actions the user is taking.

And instead of just applying them to the current state every time the user clicks a button or fills out a form or something. You could just apply it to the current state and you get the next state and the next state. But sometimes you want to record what did they do. And. To record it, you really need data because a function is a very poor record of what has happened. How do you do that? Well, it's a refactoring. When I say refactoring...

What I mean is it's very mechanical and you can do it without breaking anything and changing it. Then you get the benefits using data instead of function out the other end. I'm trying to figure out exactly what are the steps for this refactoring. You have these functions. You put the current value first and then any arguments you need after it. So the first step... is to make a new function that is hard to name, but in the pizza domain...

we'll be able to call it something like modified pizza. The pizza domain has, in this simple example, four operations. You have add topping, remove topping. set the size of the pizza, and set the sauce pizza. Those four operations make a new function called... modify pizza. It's going to take a pizza because it's a mutation function. So it's going to take a pizza and it's going to take a

Another thing that represents the operation that's going to be performed. Now what is that thing that represents the operation? It's an alternative. So however you represent alternatives in your language, that's what you're going to use. And the alternative is going to have four choices because we have four operations. And so you name... the four choices based on the names of those functions so you translate those names over and so however you do it so in haskell it would be a

discriminated union with four constructors, and you would probably take your lowercase names and uppercase them. The function names would be lowercase, but now you can uppercase them into construct your name okay in closure i would take the symbol that names the function i would just convert it into a keyword and not change the case or anything and that would be the name of the choice, right? And then each of those choices is going to have arguments that correspond to

the arguments of the operation. Remove topping is going to have the name of the topping to remove. Set size is going to have a size to set. into. Okay. So sometimes you would have three or four different arguments. We don't happen to have one in our example now, but however many you have, you have to put those into the choice for that alternative. In the body of that function, you're going to switch on the different choice. Of course, this depends on the language you're in.

Could have a case statement. You could do pattern matching. But the choice that we've got is add topic. We're going to call the add topic function with the values from inside the add topic choice. And if it's remove topping, we're going to call the function remove topping with whatever arguments are inside of that piece of data. That's it. Now it's a working interpreter.

You have a data representation of the four different operations. And you have a mutation function. The first argument is a pizza. The second argument is... some operation in a data format that you can run. This mutation function, we called it modify pizza. And again, we talked about mutation functions, how interesting they are, because you can, say, reduce over a list.

operations, and they operate on the same pizza serially, one after the other. This is a refactoring. I want to make it as mechanical and automatable as possible because I want to show that this is not some magic trick. Interpreters are not hard to do. They're not complicated. Once you've got the operations, turning it into a data representation is very straightforward. And I don't want to overcomplicate it. My name is Eric Normand.

And this has been another episode of my podcast. Thank you for listening. And as always, rock on.

This transcript was generated by Metacast using AI and may contain inaccuracies. Learn more about transcripts.