Live CEOing Ep 714: Language Design Review of Compiler Functions

Stephen Wolfram and developers review compiler functions in Wolfram Language. They discuss kernel evaluate, which runs evaluator code from compiled code and captures variables by value, and sequence type, a construct for expressing varargs in compiled functions. The team resolves documentation wording and plans guide page updates.
Chapters
Okay, hello everyone. Welcome to another Wolfram language designer view. As we get ready for the first code freeze for version 13.3 and we are talking about some compiler things. Which should I look at first? Sequence type?
Look at kernel evaluate first.
Okay.
Right, so this is, this is in some ways a sort of different version of something similar to kernel function. So essentially what kernel evaluate does is the simplest usage is it just provides a block of code that will get run in the evaluator as opposed to in the compiler, and you can use it inside the middle of your compiled code. And the other usages are just so that you can refer to variables from the compiler in your evaluated code.
Right, but why is that thing that looks like a function and kernel function? Let me remind myself how kernel function works.
Right, so kernel function is for, you know, is essentially a subset of kernel evaluate.
You have to pass arguments to the kernel function, whereas.
And subvalue as sub values.
Yes, yes.
What does kernel function do, by the way? Why on earth is that signature for kernel function not described up here?
What do you mean? That signature?
Oh, no, I'm sorry, I'm sorry.
The type is outside.
Yeah, type is outside.
Right. Okay, so kernel evaluate is taking its arguments, Is evaluating that expression, having entrained certain variables.
Correct.
Well, when you say kernel function, there, can you give a pure function as the. As the kernel function?
Yeah, yes.
That's why these are, these are pretty similar. But I think there are a lot of uses where kernel evaluate is a lot more readable.
Yes.
So, so an example of a kernel function. So here's, you know, for the one argument, kernel evaluate, you could imagine writing something like, you know, absolute time, open, close, or I don't know, something with, you know, something that doesn't take any arguments. But, but where, you know, you still want to run it in the evaluator. You know, when you run the compiler, if that makes. Or when you run the compiled code. Right, then what the multi argument cases are basically, if you want to, you know, import variables. It's basically like if you want to say, here's my compiled program, but this part right in the middle here, I want to evaluate that, not compile it.
I'm actually very confused. How does this, how does the scoping of this ARG work here?
So the outside arg. So it's okay, right? So basically arg, I mean that it's an argument to the function that you're compiling.
Right?
But that outer arg, you could also imagine that being a module variable or something like that. Any variable fit Then kernel evaluate is kind of like a scoping construct itself, where you basically say that the thing at the beginning is a bit like a width spec or something, where you're just saying here's the list of variable or maybe like a module spec. You're saying, here's the list of variables that I want to capture from the compiler environment and you know, make available for the evaluated environment.
Yes, but. Right, but it's kind of an anti scoping, because what it's doing. It's a capturing, not a. Not a scoping.
Yeah, it's a capturing.
Yeah, right.
Are you sure the argument order is right then for kernel evaluate?
I think it's supposed it should look like, with or like any of those scoping things. That's the concept. Or even like function. Function also takes.
Yeah, but remember, those are not capturing. Those are not external captures. In other words, kernel evaluate. And what you're doing is the name of the first argument is in the global environment. All those other things like function, the name is meaningless. It's a local, you know, it's a local variable.
I guess so, yeah.
So this is really a different thing. This is a capturing the name.
Well, for example, it's actually quite similar to function, except that the value of the function comes from the environment as opposed to from the argument.
Right.
But it actually, I mean, even. I mean, this doesn't matter that much, but on a technical level, I think it does use with. So like, it uses like expression replacement, like function or with would.
Right. And inside its body it's lexically scoped.
But how does it get. Okay, I'm now totally, totally confused. If I say, for example, let's say I just say, you know, I say kernel evaluate of. I've got something that says X plus. Kernel evaluate of X X comma X squared.
By the way, are you in an old. I think you're not in a 13 3?
Yeah, I'm not in a 13 3. Yeah, this is.
This is.
I mean, if I. I would recommend.
You try and run it yourself because it will compile quickly. But.
Okay.
I mean, so assuming that X is some variable in the local environment. I mean, first of all, obviously you never actually need to write that because, you know, X square is fine compiler. But. Yeah, I mean, so basically it's. It's saying X plus and then. And then here's an evaluated block, which I think was the original name of this was evaluated block. You know, and we're just saying that basically X needs to be available in the evaluated block.
Yes, I Understand. But what is different between this and function, for example, is function specifically doesn't care the name F. Okay, so look.
I understand what you're saying. I mean, arguably there's some things with block where it is aware of the things outside of it, if that makes sense.
Yes, I understand.
Especially inherited block, although that's not documented, I don't think.
Right, okay, so the point is, and, and why is it that it needs to reference that it needs to mention these variables? Why can't it just see that there are variables there and use them?
It probably can do sort of wizardry to see what variables are used, but it's iffy. And like, for example, if you have the symbolic expression X squared, but X is not actually a capture variable, you know, like there are all sorts of weird. Like for example, suppose it sees that you wrote, you know, full simplify of X squared, you know, over X squared or something like that. Right. So it doesn't actually matter what the X is. Suppose that there's no compiler variable called X in scope that. That would probably correctly be identified as an error because it would say, oh, well, I see that you use this variable X in your evaluated code, but I actually can't capture any X from the compiler environment. Right. Does that make sense?
Yeah. Surely we should say in the kernel or something in the.
Oh, we debate. Yeah, yeah.
So in kernel. So, I mean, one, the original thing we wrote was like Wolfram language kernel. But then kernel function says something slightly weirder. It says like the Wolfram engine kernel.
Right.
Which is probably a little bit wrong.
Yeah.
Then there's like, you've got kernel function open.
Another one is using the term evaluator, which is used in one of the document. One of the examples, I think for kernel function.
Well, but maybe we should say the kernel evaluator to be evaluated in the. Why don't we say in the main Wolfram language kernel.
That sounds like there might be other kernels. And also if you're doing this in some parallel evaluation, then that's confusing.
Well, I mean, what do we say that the compiler is evaluating in.
More native. Raw. Native code? I don't know if we said that anywhere.
I think we just say compiled code in a bunch of places.
Right.
Like we have, for example, things that say XYZ is suitable for use in compiled code.
I think we should say Wolfram language kernel here. I think Wolfram engine is its own branded headless animal. But let's go back to this. Okay, yeah. So kernel evaluate. Look, I mean, oh my gosh.
We, we do. We have used. And I think you, you wrote these like, look at function compile that talks about use the Wolfram engine rather than being compiled into low level code. Now we can, we can change this function compile. Right? So look at function compile and look at the notes. There's a note. Yeah, that one. There's one there.
Where?
Sorry, the.
Oh, it should directly use Dorfman engine. I think we should change that. Should directly use the Wolfram language kernel.
I. I can go and make changes like those if we agree on the.
I think we should. I think we agreed about that.
By the way, this should be kind of safe. But do we actually use kernel? Is that our terminology?
Well, yes. And look at the name of this function.
That might be true. Right. Wolfram language kernel. I will make changes like those.
Okay, all right, so let's just talk about this capturing thing, because I think it's, It's. I would say something like captures the variable X from the compiled code to be used in the evaluated code.
So I'm not necessarily opposed to that. One thing is that I think the word captures sounds a lot like closures, which is not really what this is.
But let me just go through again what this really is. This is saying, okay, let's review why it cares, right? Why can it not just lexically. See, there's an X here and there's an X. I mean, you've already declared every variable that's here. Is it true that every variable is declared?
Yes. In the compiler, yes. Okay, but write out this example. Okay, so write out, you know, imagine if your compiled function just make a function so it's clear this is the whole thing. You know, it doesn't take any arguments and then, you know, kernel evaluate of, you know, I don't know. Here's a dumb one. You know, simplify X over X. You know, close bracket plus two.
Yeah, you don't want automatic capturing. You don't.
Well, the bigger problem is that, you know, ok, so that obviously returns a three and it needs. But now if you imagine writing something on another line where you just write kernel evaluate of, you know, x plus two. The, the problem is it can't. I would argue that kernel evaluate of X +2. Like. Right. Like you kind of want that to fail. You don't want it to just sort.
Of say, now until we have a symbolic. Until we can compile symbolic things.
Well, we can. If you did typed inert expression, it would be completely fine.
Right? That's true. Right, that's. That's true. Which also makes it really confusing. So, I mean, the point being that I think it's. It is right now, basically, you know, you tell it what variables you want, and if those variables don't exist in the compiler, it fails. If it were detecting it automatically, at least right now, it wouldn't be able to have any failure case like that. So we just have to assume. Oh, I bet that's just a variable that's not meant to be symbolic. And I think that'll lead to really weird stuff.
Okay, hold on, hold on. So the basic issue is.
But we can probably. You know, I think this is a bit defensive because later we can always add, you know, inferencing like that to the one argument case. Right, but I think it would be a mistake to add broken reasoning like that to the one argument case that we can't get rid of.
Yeah, fine, I agree, but I mean, then, I mean, I think here we should say something like.
Makes X available for use in exper.
That's better. Makes the compiled variable.
Well, isn't that what passes means?
Yeah, I guess. Although passes maybe implies that it's like passing it to a function.
Yeah, Passes it by value. And passes usually is by value. Right. Which is. Which is not what's happening here. It's not passing X by value. It's making X, as you say, available.
Well, actually, I think it is passing it by value because actually, I'm trying to remember now if you actually. Tom, do you remember, like, you can't set X inside of the value, right?
You can't.
Okay, yeah, you should say so it is basically my value. Yeah, but that's, That's a fine note to have. But it's okay, so.
Would be. Fine. Captures the compiled variable X for use in compiled code. For use in evaluated code.
Use in. For use in the evaluation of expert.
Yeah, fine.
Shall I just make these changes?
Yeah, yeah, do that, do that. Right, perfect.
So to be. To be clear, also. Right, so what this actually turns into is it turns into like a with block, right? Where, you know, all the variables you declared are, you know, set whatever their compiled value is.
Variable. How about captures the value of the compiled variable X to use in the evaluation or for use in the evaluation of.
Fine.
Okay. And while we're at it, I might as well just add the note that says.
The expert cannot modify XI or something. What?
Well, I mean, by. By the time. It's sort of implied by this, but it should say.
It should say it anyway.
Right? The evaluation of exper. Cannot modify the Value value. In kernel evaluate. Evaluation of exper Cannot modify the value of X. Okay. All right. Okay, Okay. I guess it makes sense. All right. Should we look at sequence type?
Sure.
This seems. Evaluate.
Just.
Sorry, what's that?
No, what were you going to say about kernel evaluate.
No, I mean, I think I, I'm, I'm. I'm buying into the fact that kernel function is the, you know, the wrapper that has its variable values passed in. Yeah, it makes, it all makes sense.
Good.
Yep.
And I was saying sequence type is a lot more mind bending, so.
Oh dear.
Yes.
Then kernel evaluate for sure.
So this is a type construct that's analogous to sequence. So it's basically the way you do varargs functions in the compiler.
Okay.
Don't try them. For all type of. That might be related.
Right.
So basically if you look at like the first example of for all type, maybe, or I guess the usage lines too, but. Okay. Right. So if you look at that first example. Right. So here, you know, T, the variable T just refers to like a single. It's like a, A blank. A single blank. You know, it's like this is some type. Sequence type. If you look at the first example of that on the right hand side, you can say sequence type T. And that basically means that. It's like saying that it basically implies that T is actually a triple blank. And you know. Right. We can have any. You can have whatever type called blank type. So we've. One of the things we've talked about doing, which is a bigger project that is kind of theoretical right now, is the idea of trying to unify a bunch of types and text stuff with patterns. There are some arguments. I think there are various reasons. It would be nice. There are also some problems with it like that there are lots of pattern constructs that just fundamentally don't make sense as type constructs. So it'd be kind of a fake.
Subtype, probably, and vice versa as well.
So maybe. Yeah, yeah, but so. Right, so at least for now, types are not trying to look like patterns.
But look, when you do down value compilation, you're absolutely going to want to take, you know, configurations of blanks and turn them into, into types, aren't you?
Well, yes, probably orthogonal. That's, that's, that's a different thing, you know.
Right. That, that happens internally, but it's not exposed.
Okay, okay. All right, sequence type then.
Right.
So that's a type.
Maybe look at the first example because otherwise it'll be really hard to understand.
Yeah, yeah, right.
Not that the first example is particularly easy to understand. So, so basically, that type, okay, that type represents that it's a machine integer followed by some sequence of things represented by T, and it returns a machine integer.
Yes.
Then you see the definition. There are two definitions that were used for this function, right? So one of them. Right. So the first one, basically, it's like a fold. So it's taking that first integer and adding it to vararg's add of the rest. Right? And there's the base case when you give it a single integer. In that case, the sequence type is, you know, length zero, essentially. And then. Right. And then now you have a varargs function and you see that.
Why is this hard to understand it? And that, that it's a sequence type means. I mean, in terms of type theory. I understand that it's. It's in terms of, you know, symbolic expressions. Why is this complicated?
I mean, maybe it's not. If you don't find it complicated, that's great.
Maybe I didn't understand it. I mean, I, I'm. It's just a type that represents a sequence of. Of what?
Well, of anything. And then it's implied, you see, in this example, it implies that it has to be a machine integer because you see that the, you know, when it, when it recurses, there won't be a usage. You know, if you had a float in the middle of a reel or something, right. Then there wouldn't be any usage of this varargs add function which matches that signature.
Yes.
With a real. And the first argument.
Right. Because the function declaration here using that type. Yeah. I mean, okay, if you find this.
Easy to understand and that's.
I think maybe I'm totally confused, but it does seem.
I don't think you are.
Okay, well, I think this is fine. I mean, the. Looks good to me.
Great.
Is this. Are you guys putting this stuff onto guide pages appropriately and all?
Yes, I'll do that. But I just wanted you. You know, I don't want to do this. And then, fine, we rename it and I'll unfuturize these and do appropriate knitting and stuff.
Okay, I'm about to. I'm going to push back these. I'm going to do an update to push those files back.
Fantastic. Right. I'll take care of that and I'll look at the other stuff that you have as well. Okay, sounds good.
Okay.
Okay, thanks, everyone. Okay, bye.
Okay, bye.
Bye. Bye.
Thanks. Bye.
More from Guy

Cheryl Strauss Einhorn | The Human Edge: Smarter Decisions in the Age of AI | Talks at Google

Rachel Glennerster | Innovation and the Center for Global Development | Talks at Google

Rebecca Winthrop | Rethinking the Purpose of Education in the Age of AI | Talks at Google

Dr. Robert Wachter | A Giant Leap: How AI Is Transforming Healthcare... | Talks at Google






