1 >> Tom Ball: Okay. Welcome, everybody. I'm... here from Texas A&M where he's a professor. He...

advertisement
1
>> Tom Ball: Okay. Welcome, everybody. I'm really pleased to have Gabriel Dos Reis
here from Texas A&M where he's a professor. He has a very interesting history. He has
a Ph.D. in mathematics from Université Paris-VII, sorry, and École Normale Supérieure
de Cachan, France. And his research interests range from mathematical software, applied
formal methods, programming languages and libraries to generic programming.
In 2012 he received an NSF CAREER award for his research in compilers for dependable
computational mathematics. He's an active member of the ISO C++ standards
committee, and he's authored and coauthored several features of the 2011 ISO C++
standard as well as doing research in axiomatic programming and his OpenAxiom system
for scientific computation, and also at some point being the release manager for GCC.
So can you imagine all of that in one person. Here he is to tell us about his recent work.
>> Gabriel Dos Reis: Thank you much, Tom, and thank you for inviting me to the
lecture.
So, yes, when people ask me what I do, I say I do programming and I say I also do math.
And I'd like to tell you today why I believe that doing programming is actually doing
math. For a moment I have to qualify, I have to say doing good programming is doing
math, but eventually I hope that we'll just say programming is math.
So in this talk essentially I want to give an overview of what I'm doing in programming,
[inaudible] math, and I started from computer algebra. And it is all connected. And I'll
try to explain and try to convince you that, yes, this is very connected, and then I'll focus
near the end on my -- one of my recent projects, which is Liz. It's an experimental
language for doing research in structured generic programming.
So I started as a student working on geometry and then get seduced by using computers
to do geometry and then quickly spent too much time in a computer room and now I'm
going to back again.
Okay. So I did internship [inaudible] at INRIA working in European Commission project
called Frisco which was a project to write libraries to solve huge polynomial systems that
people found in science and engineering.
And I was mentored at the time by Victor Pan, who is one of the experts on structured
matrix computations and so forth. And had to work on the one of the fastest algorithm
for uniform approximation of univariate polynomial roots.
And at the time, first of all, the project consisted a few working at INRIA, Sophie
Antipolis [inaudible] northwest and people in Italy and Spain. But essentially we had
only two implementation languages, Fortran and C++. So I didn't really find myself very
comfortable with Fortran so I chose C++. And this '96.
So you have to remember at the time I had GCC, but it was horrible. And the other
choice I had was Sun Microsystems compiler, CC. It's pretty good. And in the end I end
up actually reimplementing a component of the C+ standard because it wasn't part of
GCC and I need it to get the library done so that my colleagues could use my library
without having to reinstall everything.
2
And while I work on the implementation of valarray I found several bugs, and the only
way I could actually get them fixed in the center was to join the AFNOR group, which is
the equivalent to the NC group, or French group, and then no [inaudible] valarray, GCC,
and as a punishment they asked me to become a tenure and later become Liz manager
from 200 until 2007. We stop because can't be professor and Liz manager at the same
time.
But what I got out of it was real interest in supporting generic programming. Because a
lot of computer algebra and using C++ of course templates, discover that you need to do
something different. And the reason why I got in GCC was I thought, well, it's just a
software so it should be fixed. And that turns out to be a bit more complicated than I
realized at the time.
Okay. So I'm going to show the connection between generic programming with C++ and
computer algebra.
So generic programming is essentially methodology for developing reusable form of
libraries, and you want efficiency and really want to parameterize the components so that
you can reuse them and you want to manage complexity.
And so my background is in computer algebra where we have [inaudible] mathematical
structures so it is essential that we find a way to manage complexity.
So since you're here we have one function called find. I can use it in a vector, I can use it
in lists, I can use it in any dimensional sequence. And it just works the same. I can use
the same wisdom working on vectors or list.
Now, why is that important? This is important because it is a principled way of
constructing software. We need principles. We need things that scale.
And the other thing is that actually it is not just some [inaudible] model methodology
[inaudible] it's just not mechanics, playing with for all or exist. It is there, real code.
You know, you here at Microsoft, Adobe, Google, Bloomberg, A9. One of the things
that's common with Adobe and A9 is that Alexa Stepanov who invented [inaudible]
worked Adobe till two years ago and now he's at A9 and still working on general
problem and trying to get -- educate programmers to get things done.
And actually it's effective. There are many places where object-oriented programming
[inaudible] in the '90s failed where using generic programming actually succeeds pretty
well. Languages like Java [inaudible] started as pure object-oriented and then they added
generic as the reason for that.
Now, a connection with computer algebra. So actually computer algebra is one of the
original areas of generic programming because of the complexity in real structures that
mathematicians, computational mathematicians have had to deal with.
You can think of GCD and if you think of the Euclidean algorithm, the state of the
Euclidean algorithm is very abstract if you have Euclidean domain. Essentially a
connection of things that look like numbers, you can divide them and you can have a
3
process that tells you when you can terminate, so you have some clear termination
criteria. You can do GCD there.
Or you wanted to compute Gaussian elimination. This is what people who do linear
algebra like to do it, you can do Gaussian elimination with floating point numbers which
is kind of approximation of fields. But what if you don't have division? Well, it can still
work, you know, use Gaussian elimination on rings that happen to have the right
properties.
So you see that if you set up your algorithm and you understand the conditions and very
well be you can get a variety of complexity and very good algorithm that actually scale.
And the last thing, and I probably said before and I'll repeat it again, is that mathematical
structures are very, very complex. They're sort of the things that you learn. Math is
great, but I bet everyone would understand that there's a lot of complicated stuff out
there. And generic programming helps with complexity.
There are many ways of doing generic programming. If your language is Haskell or
more [inaudible] oriented, there's going to be a lot of good stuff out there. And there is
now several experimental systems to look at several ways of doing generic programming
from Haskell point of view.
Most of these have in common a focus on the orientation of data. Okay. And I try to
explain this [inaudible] or sometimes situation from category theory and type theory.
And this is actually what you'll see in most academic papers. And then there is the
[inaudible] aspect that where you focused on algorithm.
So as I said, if you look at computer algebra, it is mostly having to do with algorithms.
So Stepanov explained this [inaudible] STL using [inaudible] templates. It focuses on
algorithms [inaudible] when you go to industry, that's what you find predominantly, even
whether you use C#, Java, its focus on the algorithm. And I learn it from the computer
algebra community.
And the idea here is that you may have a general formulation of an algorithm, but you
can add a series of requirements and refinement to have better complexity.
So, for example, if you want to search an item in a sequence, you can do linear search.
But if you happen to have a -- if you have a single link list, then you have to do linear
search. But if you, let's say, have an array and you know that the array is sorted, you can
do binary search and there you have better complexity, on average, than you have linear
search.
The connection between data type generic programming and algorithm-focused generic
programming is still something that's done. In the Haskell community [inaudible] Peyton
Jones and Ralf Lämmel have all been trying to bridge the gap. And so we have these
paper series of [inaudible] try to bridge the gap. And Jaakko Järvi and myself trying to
bring some ideas from Haskell into the [inaudible] community because it's always good
to get infected by a lot of people.
>>: Can I ask a question?
4
>> Gabriel Dos Reis: Yes.
>>: So for the example that you mentioned about changing the implementation from,
say, a -- from one representation of an array to another, I mean, that kind of thing is very
naturally done with type classes is Haskell, right?
>> Gabriel Dos Reis: Yes. So ->>: And I wonder about your view of your statement that generic programming Haskell
is about data. It seems to be at least for type classes it's more about operations that that
data supports.
>> Gabriel Dos Reis: So if you look at type classes, type classes focus on type, not on
algorithms. The -- I'll have some slides on the contrast I want to show. But if you give
the example of sort algorithm in C++, if you want to parameterize your sorting
[inaudible] function, you can do it in Haskell, but you start getting into hoops. You have
to use multitype-type classes which are not standard.
And once you get there, you have a lot of ambiguities in how you solve that. You can try
to use functional dependencies that gets more problems. You can try to solve this
problem in Haskell, but as you do it, you're building more machinery complexity which
is making this program more and more complex.
Because at the very beginning the focus was on type, not on functions. Yes, you can use
type classes, but you are going to only go in one direction, not try and have at least twoor three-dimensional view of what the problem is.
>>: I'll wait for more ->> Gabriel Dos Reis: Yeah. Sure. We can have more discussion on this. But, yeah,
very good question.
So going to give brief example of interaction application of generic programming in
computer algebra. It's algorithmic differentiation. So we all know this thing about
differentiation when you take a calculus class, which is, you know, we have [inaudible]
law, and so it sounds very simple problem.
The problem you're looking here is different. It is that you have a program that computes
some mathematical function, and you're right to have another problem like computed
derivative of that problem. So you don't have a closed formula. You have an algorithm
that computes that function. And you like to generate automatically. And in a program
that computes it already. So that's algorithm differentiation.
So transformation phi here, if you accept that you have some semantic function that gives
meaning to your program in some mathematical domain and you liked -- and you have
the mathematical properties, they can get derivative, and you like to have a
transformation of your source program to something else. If you take the meaning of
that, you get derivative. It's what you're looking at.
5
Well, the first thing is this is not divide the difference. Meaning this is -- it is not
computing the value of a program, a function, two different point and then divide and -by the difference. This [inaudible] that scheme is very unstable and not very interesting.
It is not taking an expression tree and just apply Leibnitz rule indefinitely. Assume you
have a closed formula. This is about you mention you take the [inaudible]
implementation of square roots. Okay? And you would like to have a function that
computes the derivative of that that's done automatically, not using different equation.
So if you want to do that, the first thing you need to do is to get a better understanding of
what is going on. And you have to work at a very high level, not the concrete
representation but a very high level set of properties of your function and also have a
[inaudible] semantics of your language so that you understand how control flow is
expressed and work at the mathematical level and then design a transformation that gives
you the actual representation. So it is something that I did, I work on with my colleague
Jaakko Järvi and student Jacob Smith. He's now at Intel.
So, one of the reasons why this is actually a good thing to do is that most functions we
use in size, they don't closed form. Now, I want to take at least zero, two, or three
classes, they always come with very nice functions and they teach you how to compute
derivative.
So you move to the real world where I have to supplement, and suddenly you realize,
well, most of your functions don't have closed form. Okay? They're probably defined by
some differential equations and you devise algorithms to compute the function but you
don't have a closed form.
And sometimes, even if you have a closed form, you don't really want to present it as the
expression tree and derive it. If you take this sequence series of functions, so we have
zero, we have the identity, and then the old thing [inaudible] it's like Fibonacci function
but it is not because you have some log there.
The interesting thing about log is that when you derive it, you get quotients. And it keeps
going on. And so, well, if you derive -- start to derive computer derivative each element
here, essentially the representation doubles at every step. So you don't want to that.
Whereas if you resolve it as an algorithm that computes, the algorithm is compact, fixed
size, doesn't change. So you prefer the algorithmic representation over the expression
tree of presentation for doing the [inaudible] computation.
And the rules I actually use is not the Leibnitz rule that says essentially this thing plus
linearity. What's actually used is the chain rule. So if you mention that you have
sentiment S1, sentiment S2, you run this on the machine, essentially transformation of the
statist of your machine.
So if phi is this function in the transformation that compute the derivative, well, you have
to use the chain rule. If you compose F and G, then the derivative of this whole thing is
the derivative of F, apply what was there earlier, which is function F1.
So this is what you use in practice. This really sometimes you want to have X times Y,
6
you probably need that, but we'll [inaudible].
And this also implemented as a library. And the reason why this is very successful is that
in the OpenAxiom system we have this notion of categories supposed to be equivalent to
type classes except that they are mostly parameter type classes and well behaved. And
the whole library in Axiom is generic and structured in a very mathematical way. And
Axiom was designed to prove a point, which is that you can do structured computer
algebra with strong typing. If you take Maple Mathematica, it's not the case.
Mathematica you have expression tree and you substitute to get something. If you get the
answer, great, most every time you get something; you don't know exactly what it is.
Maple is close to that.
And it turns out that this actual paper was an improvement over what existed in Maple in
2007. I think they are beating us now. But in 2007 it was a huge improvement over
what -- yes.
>>: Are you assuming that the functions are continuous?
>> Gabriel Dos Reis: Very good question. So the question was whether I'm assuming
the functions are continuous. Not only I'm assuming that they are continuous, I'm
assuming that they can be different theories.
And if I remember, I'm assuming that the algorithm representation is very natural. So to
give you an example, if you take the identity function, you can decide that, well, it's F of
X equal return X, something like this, or you can say, well, it is return X except if X is
42, in which case you return 42 you have a sweet statement, so only you have some kind
of artificial discontinuity. Right?
So I'm not assuming any of those things. It is a restriction on the form of the input
program. In practice, it is not a big deal. Because in practice most people write functions
natural way, and if you write it in a way that is not natural, if you write it, well, I'll return
X except if X is 42 then return 42, you'll see that you get derivative -- if you apply the
rules, you'll get 1 for everything except for it you will get 0 because it's custom, right?
But, yeah, it is one of the shortcomings. And I was hoping that, you know, [inaudible]
continue and try to use ideas from abstract interpretation to try to -- kind of symbolic
limits at some artificial discontinuities. But yeah. And in practice, again, this is not a
real -- is not a big deal because most codes there are written in appropriate way.
But you handle four loops like you read in C. So things just work beautifully. It's
amazing. It's very, very simple and amazing.
And another thing is that -- sorry. The -- huh. Yeah. So a very different situation
[inaudible] numerical stable. It inherits the same array so we can actually prove a
theorem if you write down the informal rules that if you do this program transformation,
the numerical error you get is the same as, you know -- the accuracy is the same as the
original input. So it is actually preferred way. And as a matter of fact I wish that when
we teach calculus classes, we teach the algorithmic way of differentiating a program than
the formula that people learn. It is very good work in an algorithm.
7
And the rules are very, very simple. Only need one general. And you can do computer
science programming and calculus at the same time.
Okay. So next thing. How do I get from -- so during programming I'm doing that for
C++, also I did that from there to the other part of my research which is trustworthy
systems. There are just two slides because I want to move very quickly to this.
The first thing is I really like to trust my libraries. First I want to trust the
implementation of some library and I would like to trust your library. And you probably
want to trust my library and how do we get there. It is program correctness [inaudible] is
really easier if you know you don't depend on some implementation details. And that is
assured by working at the most abstract level. So you need to generate programming.
And another thing is that I actually believe that C++ is a fine representation language.
Okay. I was raised in France, so in the good French tradition I had to learn Pascal and
the next was OCaml, of course, from Guy Cousineau who among the people who -Francesco can tell you more -- people who are at the theoretical foundation of, okay,
well, so Caml stands for Categorical Abstract Machine Language, and [inaudible] but
does come from Guy Cousineau [inaudible] and so forth.
But C+ is very fine language to work with. You just have to be principled and
disciplined. So one project I have, longtime project is to try to build a formally verified
C++ compiler the same way that we built the CompCert compiler, which I think is a very
good thing. It's for both the C community and the formal methods community, which is
that, well, hey, you can actually build complex stuff and improve it to be correct. So it's
good stuff.
I would like to do that for C++, too, because the language, it's complex, but there are key
things there that I -- that I would like to see more in mainstream languages. The first is
the notion of construction/destruction. Many of these oriented language have the
construction, but they don't have the deconstruction part. They have [inaudible] and it's
messy.
Systematic reliable destruction [inaudible] resource is something very important. You
can depend on it. And it is something that I think should be preserved in official
languages. So with Xavier and Tahina we formalized that notion. We've got dynamic
location but just know the principle.
It was originally introduced by [inaudible] in early '80s, but [inaudible] to be correct, and
in the process we found some bugs in the standard. So this is one of the cases where you
get some academic paper and you fix the real-world thing. So it's really good. And then
the bugs have to with virtual function [inaudible] during construction.
In the next step I would like to formalize -- sorry.
>>: I want to go back just a little bit to the stuff on differentiation, and started to make
the connection that how does this relate to the generic programming stuff you were
talking about? So far should I understand this differentiation stuff as an compiler that's
transforming abstract syntax trees to abstract syntax trees?
8
>> Gabriel Dos Reis: So the concrete representation and implementation was on a C
library. But it works at a fully typed abstract semantics graph, not abstract syntax free. I
wish we say abstract semantic graph as opposed to ASC, because, you know, when you
have the representation, it is no longer a tree, it is really a graph. And it is typed because
types give meaning to the programs. Okay. When I have integer and I say, well, integer
satisfies a notion of ring, that's not the same as having string.
So being able to distinguish an integer from a string is very important. So you don't just
do some transformation on expression trees, you look at a type and this satisfies some
constraints which is that you have to have ring, not just a ring, you have to have a
differentiation of ring. Not only that, you ought to have an algorithmic differential ring.
So the details actually in the paper. I'll -- sorry, I was going very fast on this.
>>: [inaudible] provided a way to introspect on the ->> Gabriel Dos Reis: Yeah. So if you go to the OpenAxiom Web site, you actually
get -- I developed a library for program analysis. So it has -- it comes from the syntax.
The compiler is available at runtime. So, yeah, I didn't go much into detail about Axiom,
but it has parameterized types. It is theoretically typed. But types are instantiated at
runtime which means that you actually have [inaudible] of types at runtime if you wanted
to have introspection. Yes. The compiler is available at runtime. But the program is
aesthetically typed.
>>: But don't you worry about things like breaking abstractions, like if I find a function
and I can introspect on its syntactic structure, very week notion of abstraction in the -that is not a problem. One, you're doing symbolic mathematics. Because what you have
you see [inaudible] X is of type 8. But that's something you discover at runtime.
Interestingly, when you do math, if you want to formulize that, it is most of the time
runtime. If you want to do everything statically, you get into a lot of trouble. If you want
to integrate a function, you first need to understand whether that function, for example, is
rational, is elementary, or it has some -- defined by ODE. This is something you don't
just look at the form of the function and say, oh, yeah, it is ODE, you try to put into the
function. This is something you do at runtime by looking at this structure, running the
function over some kind of test point set of polynomials or set of equations to see which
one it's the root of, it's the zero of, and then discover that. You come back, oh, here is
actually what it is, and then you go on.
So it is a lot of dynamic thing going on. It is not -- computational automatic is not static.
Is it very, very dynamic. So I'm not worried.
And, yeah. I can tell you more about OpenAxiom, how it works. And study typing is a
shift through a -- two-level type system where you have domains that are instantiated at
runtime, and then you have categories that will be cumulative-type classes that you use to
predict what might happen at runtime. And since you do your reasoning not on a type but
on the abstract mathematical structure, you can still do type checking. And you don't
break abstraction because things like concrete representation is not accessible at runtime.
What is accessible at runtime is algebraic properties and [inaudible] properties. Thanks.
So, yeah, I was here. And so now the things that I would like to work on, well, of course
9
I like templates, I would like to have more formal accounts with templates, very
complicated, I know, but I think we can get there.
And one thing I want to announce is that I have a new student for work who has been
working on code extraction from tough development to C++. And he's in C++ 11, and it
makes things much simpler. He's quite well advanced and I hope that we soon will have
something that is part of the Coq main distribution as opposed to just being he and me
having something we put on the Web but part of the -- that way I could develop part of
libraries in Coq, get them [inaudible] to C++, integrate that into my programs and say,
well, I can say this part of my C++ program has been proven to be correct.
Okay. So next thing. So how I go from trustworthy system to Liz and computer algebra.
So Liz is this experimental language I'm working on. And interestingly enough it brings
me back to computer algebra. Not that I want to solve polynomials, but trying to write a
complier for this language brings me back to old problems that I was trying to avoid.
Okay. So what is Liz. So it's system for structured -- what I call structured generic
programming. The core semantics is based on that core of C++ I think it is okay. There
are a bunch of things that you can find complex about C++. There is a core that's actually
well designed that I think is good for -- as a basis for a new polynomial language.
It is inspired by the book by Alex Stepanov that was published in 2009, "Elements of
Programming," where he tried to explain that generic programming is really, really math.
And of course I know I was hugely influenced by my experience on using the Axiom
system. The first time I hear about the Axiom was when I did my internship at INRIA,
was '96. Then I became a developer maintainer of the compiler. And so there was a lot
of implementation techniques that actually should find there way through mainstream.
So it's a good polynomial language I believe for starting efficient and scalable generic
programming and lessons in terms of design and implementation technique. I would like
to use that to second effort at designing C++ concept. We probably were in the C+ OX
we tried to have concept for C++ which was kind of type system for templates. They get
in the standard draft and then they had to be taken out again because of various problems,
some of them, really, to growing complexity. And this time around Ben and I got funds
from planning from NSF to do this thing again properly. And I believe you know how to
do it, but I don't want to have all the complexity of C++ while I'm looking at this
problem. So I'm taking some part of it and looking at them in this system.
And the other thing is the constraints that we put for C++ concepts, we believe that for
this to succeed, we have to break with conventional type checking. What I call by
conventional type checking is you have a nice interface and you're going to represent this
by some kind of V tables and you're going to [inaudible] V tables at runtime. And so
we're going to fly for C++.
The reason I'm going to fly for C++ is that if people have to move to improve system and
lose inefficiency, they're not going to do it because the disciplined programmer knows
how to use templates effectively. What you're trying to do is get the masses do the same
thing, get the benefits. So we have to break with conventional type checking. And it is
being developed as open source on a BSD-type license when you just look at the codes
and do whatever you want to do with it. That's fine with me.
10
So it started with Carla. She was at the time one of the grads. I had her write the first
parser for Liz. She's now at Microsoft as programmer manager. Then after that Erik
Katzen. I had him rewrite part of designer library using that facilities that I added. And
based on his work, I decided to rewrite the parser myself and change completely the
syntax. So since last summer I've been busy rewriting parser.
Jasson is a Ph.D. student with me. He's actually from ECE being coadvised. And he's
interested in network and secure network protocol design. And he wanted to use the
ideas from Liz to look at goods designed for the network. And Jordan Donais is
undergrad doing research, and he has been very good at finding bugs in latest
implementation.
Now, the first thing is that I'd like to go back to Hindley-Milner, and I suspect this
probably is related to some of your questions about Haskell.
So we have ideal in programming language which is the Hindley-Milner Type system.
It's a masterpiece. It is great. And it has only four rules, sometimes, right, five.
Originally it was written five, but it can have four rules.
And it was based on very simple ideas. Look at your programs and what you're doing,
essentially, you have functions. You apply them. And you have variables. That's
essentially all. And the idea is that you don't need to write types for your program, just
write your program. And based on this structure, we can tell you whether it is waveform.
We have rules. It is waveform. And the compiler is always the winner. It's going to
infer a type and if you can supply the type.
If you supply the type, it's going to be a smaller version of what a compiler can deduce,
small version in terms of instantiation, going to instantiation of the most general type. So
what the first is always the best you could possibly write. So you write your program,
your types, and if you believe that type give meanings to program, then the compiler is
going to find meaning for your program. And because can have several type, that means
can have several meaning. It's a very simple idea.
But what happens, well, it's [inaudible]. What happens is that it kind of puts the focus on
some kind of a bottom-up view of how we look at types. Usually we take most
traditional papers on type systems. They're going to give you some initial algebra and
here's the way you construct things, then it's what did you do with it.
One problem with that is if you try to develop a generic library very rapidly, very
quickly, you run into having several type parameters. So just as an instance, when
people, you know, develop type classes for Haskell and they wanted to write container
libraries, they realize, well, they need more than just one parameter, because containers
happen to carry a lot of things we don't talk about for most of the time.
And so McJones introduced the notion of multitype parameter -- sorry.
Multiparameter-type classes. And originally sounds like a great idea until realize there's
some problems with ambiguity. So he introduced functional dependency. They said
well, we can have two types, but one is going to determine the other, and then you can
solve ambiguities. But only partially. And things started, you know, following and so
11
forth.
Eventually in the 2000s people decided, well, why don't we just go back to those C++
guys and borrow some of the ideas, ideas being top traits. That's how you get [inaudible]
types.
So when the paper on [inaudible] types were published originally explicit information of
the inference of C++, and I thought that never again because now people have to refer
back to the more formal version.
So the point here is that when you base your system on a Hindley-Milner Type system,
which is very nice, and you build generic libraries, you get a lot of extra parameters.
That you have to mention again and again and again. It is a distraction. It makes the user
interface, the software complexity very thick.
This is Microsystem. This isn't off the system. [inaudible] good, it's just that when we
want to build generic libraries, it is complicated. So it is not just familiar. The same is
true for C++. I'm going to show you an example.
So I'm going to present something, propose something different. So it's my experimental
system. There are ideas there that probably don't scale. But I do believe there is a good
idea there that we should look at and actually go inference the rest of the languages.
Okay. So the first thing is when you write your program, your library, think of it as a
mathematical object. Even if you're not [inaudible] think of it as a kind of geometrical
object.
Here you have a surface and you have some -- this sphere and you have some curves,
algebraic curves of degree over 40. And how would you describe this thing? Would you
like to use explicit parameters to describe it? Or would you prefer simplicity questions.
Take this sphere, for example. A simplicity question you just need one. You just say,
well, X square plus Y square plus Z square call [inaudible] square. Some number. That's
all. If you prefer the parametric version, you can just use one system of parametric
equation to describe the entire sphere. That's just a mathematical theorem. You need at
least two.
Now, if you need two, you're going to have relevancy. Now you need covariance,
conditions between. It's complicated.
What I'm saying is there is some analogy here between this situation and how we develop
programs. Look at your generic algorithms. Are you happy about them? Do you have
your right number of parameters? Do you have two more? A lot? Few? Just enough?
How do you specify constraints. Okay?
Now, if you take the [inaudible] parameterization [inaudible] this is a parameterization of
this sphere, I'm just using the [inaudible] projection. And compare that with the equation.
This one is very nice work if you're just looking locally at a portion of this sphere. But in
general it is a little bit complicated.
Okay?
12
On the other hand, the implicit equation is very nice, just one thing, all the other points at
equal distance from a center. Good. Okay? But sometimes it's very hard to get things
like what's the tangents to the sphere at a certain point. That's nice to do there to here.
Okay?
So what actually happened is that you can't just say one is better on the order
systematically. What you have is different ways of looking at the same problem. And
mathematicians have been very successful at this because they've developed tools to
convert from one to the other. And you need combination to be successful.
Now, this one from here is that I would like to propose a way of looking at type systems
differently from the initial algebraic view of type systems. So that's what I'm going to
talk about next.
What I want is some kind of co-algebraic view. So each algebra essentially have some
structure. And you look at it. The co-algebraic, you poke, you ask questions, can I do
this, can I do that. If you look at the ST, for example, one of the reasons why it is very
successful is that it is purely co- algebra. It doesn't have any memory allocation. Okay?
It is very functional in nature. Even if it is written in C+, it is very functional in nature.
But it doesn't have any memory allocation. You copy to another place. The iterator
knows how to allocate. It is not the algorithm's job. Okay? Do you know transform?
You [inaudible] many things are done without knowing how they're done, just you have
to specify what's going on. Okay.
So here is what I don't want people to do with explicit parameter. So I don't make this -I don't make this up. You can take GCC and you look at the internal implementation of
the hash table. This is what you get. You get at least 11 template parameters. How do
you know you have them in the right order? How do you know you have all of them
correctly? I'm not -- this is not artificial. It's taken directly from the GCC source code. I
don't touch that part of GCC because I think it is just insane. So I don't want this. So if I
don't want this, what should people do?
>>: This is pretty recent code, right?
>> Gabriel Dos Reis: I'm sorry?
>>: This is pretty recent code?
>> Gabriel Dos Reis: Oh, yeah. So yeah. If you -- I'm sorry?
>>: Because [inaudible].
>> Gabriel Dos Reis: Possibly. Or just look at -- you know, I have a copy of GCC on
my hard drive, just look at it and -- I'm pretty sure I can come up with something similar
to this in high scale. Okay. But I thought this was much easier.
The fact that this is recent is not the issue. The fact is that this is how people write it.
Yes, you're right. Hash tables were not in C++ 3 and they are in C++ 11. So this is
recent version. But the point is this is what people do because they want to control every
13
part, so they have to pass at least 11 parameters. They have to mention them -- so here -so everything is real expect this dot dot because they are his parameters and they won't fit
on this slide, so just written like that. But, you know, if you have the source code, you
can look at it. It's ugly. But apparently it works.
So I don't want this. What I want is the first thing is we spent so much time especially in
object-oriented programming world asking what is this thing as opposed to how can I use
this and when I use it, what does it do. That is actually what is most important. The STL
is successful because it does not ask me to be something. It just say this is your
conditions. When you use this thing, this is how it should -- all it should produce.
So you focus on a behavior as opposed to some kind of glorious state of being something.
Okay. So, for example, if you take this soft function from the STL, it takes two iterators
that designates some sequence. And you want to sort. You can provide your own
[inaudible] function. Here is the comp, so it is just some kind of -- it is value of some
type.
And I can use my own comparator that way or I can provide point of degree of function.
It will work great. The problem is if I messed up and press a wrong argument, well, I get
very terrifying error message and I don't know where to start. So the question is how can
I specify constraints on these template parameters that reflect what they're supposed to
do, not what they are, but what they're supposed to do.
So to look at that problem, well, we try that in past decades and didn't work exactly as we
wanted. I'm very persistent. You had a question? Sorry. Oh. Very persistent, so I'm
trying again. And so I want to isolate one aspect of it and study it in some experimental
language, gain some understanding and see what scales and get that back to C++. Okay.
So the idea is to impact a real-world language just for some language but looking at
fundamental problem.
So the call to write field is that, well, I'm going to look at type checking differently.
When look at type checking algorithms, there is certain questions we ask implicitly. I
want primitive for that [inaudible] language so that the programmer can state what's
going on. So the first thing is understanding something that can call from something that
shouldn't be called at all. So call that function.
Then function, if I have [inaudible] function, well, I have to know whether I'm calling to
the right number of arguments. So I need a primitive that gives me arity of a function
type. Then I want to be able to look at its argument positions. So this is also -- an input
type is an operation, binary operation that gives me the type of a parameter at a given
position. And then the return type.
So if you -- again, if you look at, take a geometric view, essentially a function is an object
of N dimension or N plus 1 dimension where N is the arity and then you use input type
and codomain ask the coordinate access to talk about what's going on. Right? Okay. So
that's -- it's that geometric view that informs what I'm doing.
So, for example, if I take the functional type, now a point vector and I construct a vector
field, for example, one of the arity is 2 and the input type at 0 is point and input type at 1
14
is vector and the codomain is vector field. So that's essentially what these primitives are
about.
Now, my claim is that these four primitives, and five because I also need a way to know
when two types are equivalent, are sufficient to conduct type checking of an algorithm at
most abstract level. So that's my claim. Okay. And that's what's the basis of the latest
project.
So here I have the mathematical, so, you know, I want first to prove that you can actually
have a mathematical statement of something and have that encode that surrounds. So
here I take notion of a homogenous function. So a function type is said to be
homogeneous if it takes at least one input type and all its input types are the same and we
don't say anything about the return type.
So the arity must be zero -- must be positive, sorry, and for all inputs type you have the
same -- here I have the equality. Earlier on the previous slide I say how can I say two
types are equal. So have relation of equality of type.
So here I'm defining what I all a concept. A concept essentially predicates over
something. And that predicate is supposed to define an entity in the most fundamental
way. Anything else I should derive that from the definition as opposed to having it in my
head or in some documentation. Okay?
So here, for example, if I take the function distance between two points, well, it is
homogenous function, but if the function translate that translated point by a vector and
produce another point, it is not homogeneous function because the two input types are not
the same. But distance is a homogeneous function. Even the return type is different.
So this is what this notion of concept says. Okay? So essentially I have sequence of
requirement predicates. You can think of a concept as a predicate over its arguments.
And, again, my claim is that concepts would consider basis of type checking.
So if all the input type are the same, actually have the notion of domain of the object
here, I have the row that gives me -- I can define definition of domain, just like a role, I
can think of either some kind of -- something I added to C++ 11 which is template ASs, a
way of having function types, type functions; namely, the function type directly.
Or way of doing computations with values and types at compile time. So those two
things are in C++ 11. I implemented myself this one directly, GCC, so it is a [inaudible]
recent version of GCC, and this one was implemented by someone else.
Now, an operation. What is an operation? An operation is a homogeneous function that
have the same where you're doing type is the same as input type. So here I can state
succinctly what that thing is. It is code. So the compiler understand it. Compiler come
easy to do type checking. That's the good part. And, again, I'm using type equality, an
example of an operation. So if my multiplication is nonoperational, it's an operation, but
computing normal for vector is not an operation.
And binary operation, now I have to fix the arity here. I don't fix the arity. Now the
input types are free to go in direction, but they're all the same. So I have some kind of
15
[inaudible] view. But here I fix a number of parameter. And, again, these are sufficient
to do type checking.
Now, here is a function called square. So if you know -- if you have an operation, you
can actually use it to do [inaudible] squaring and so on. So here just have a function that
takes an operation. And a value of an int domain and return that function -- sorry, that
binary function apply to the argument twice.
So if you apply to call these functions square with three and multiplication, you get nine.
And if you call it with three in addition you get six. It is basis of [inaudible] squaring
algorithm that is so basic.
And interestingly the type of this function is, well, I take some type alpha and I require
that it satisfies a predicate binary operator and any input types are not referenced to const
D, the domain, and alpha.
The thing here is that the real parameter is what I want to emphasize, which is that I'm
applying an operation to change an int domain. Not the parameter -- not the domain type,
but the operation. This is where I want to put the emphasis.
If I want to use the classical view, then I have to introduce this, ref const D as the
parameter, and then I have to somehow specify directly the representation of the domain.
I don't want that. I want the representation of the function to be as abstract as possible.
Okay. So here example. I can apply some multiplication or addition.
Now, more on this. So it is customary to every time want something to define a type and
say, oh, I have something of that type. But type, I just carry a set, right? They help us
represent values. And carrier set could be carry set for many algebraic structures. For
example, if you take monoids, for example, your notion of monoids, which essentially
have a binary operation and then you have one element when you combine [inaudible]
just return that element and then have [inaudible] things going on.
Well, integers, infinite integers [inaudible] forms a monoid. But if you take infinite
integers and GCD, for example, it's a monoid structure. So you can't just say integer is
the monoid. We have to be very precise. You have to say for which operation.
And then you see that, well, the property is very interesting, but what you want, you want
to put them on operations as opposed to the carrier sets.
So here how do I set an operation is [inaudible]. I can set that specifically. So this is of
taking right encode, get the compiler to check the definition, and at some point I can
inform the compiler that, well, operation -- actually I'm going to assume GCD is
associative.
I could -- in a more advanced system, I could actually provide a definition of GCD and
try to prove that GCD, but I want something that can scale, something that I can scale, I
can get back to original C+. So I can have everybody do proof. Most people prefer to
write algorithms, not proofs.
16
So the type checker, I can also have the type checker use Axioms to design semantic -runtime semantic properties to aid with other resolution. So if I want to have a notion of
semigroup modeling, as I said earlier, so I need a notion of semigroup. So a semigroup
operation is parameterized over an operation. And I need a type for that operation, so I
put a [inaudible]. Can think of these as the templates. So essentially it's the guessable
type. What area it wants to apply is the operation I don't care about the type, but I need a
type so that I can get some other things moved.
And then I can define a notion of neutral value, so it's an Axiom. So and if I -- there's
some value E and if I put it in different places, I get the other stuff back. And now I can
state what it means for an operation to be a monoid operation.
Well, essentially you need to have some distinguished value. The neutral value had to
satisfy that property. And, by the way, I would like to be able to refer to that later on, so
I introduce a rule that says, well, I want to name that neutral value E later.
So implicitly this is actually a function of the operation because I have some extensions
here, I get [inaudible] and it turns into a function.
This is a principled way of doing type functions or associated values. Currently in C+ we
have traits or in Haskell you have type families. And you state axioms. They are not
even controlled. You can control, but most of them, they are not controlled. And they
are the root causes of many of the headaches that Haskell people have, and many of the
headache that similar people have trying to build a skill generate programming.
We want type functions, but we want them in a structure. We want to know when you
introduce a type trait at a given point what's the controlling condition. It is there. So it is
not something that is defined by any programer willy-nilly. It has to satisfy some
constraint. Any complier is going to check that.
So what do I get out of this? Well, first thing is that I got shorter and clearer codes. It
have higher level specification and it aids verification. And I really want to get rid of
these things. They look nice, but they are not good for software construction, at least
scalable software construction.
And so one thing that's quite interesting was I had a student [inaudible], and I said, well,
let's look at this in a [inaudible] system. Because, you know, we have [inaudible]
structures, we should be able to do something with it. So the first thing is can we do
some form of low hanging fruit, some kind of implicit parallelization.
Many operations happen to be [inaudible] in [inaudible] setting. So can we get this thing
actually. So at the PASCO conference, we were able to quantify opportunities for
implicit parallelization if we can inform the complier [inaudible] properties like
[inaudible] and so forth. So GCD, which we use very frequently in [inaudible] system
solving is [inaudible]. And then in a subsequent work, I got him to actually get the
compiler [inaudible] and generate code implicitly. And it was pretty good. Yes.
>>: Do you have a result for the computational complexity of type checking?
>> Gabriel Dos Reis: Yes. I hope soon. Yeah. Yeah, I know I'm running a little bit
17
time -- yes. I'll get to that. And this is where it is still ongoing work. And then I would
like to see what scale and put it back. But I do believe that reducing the number of
[inaudible] is good way to go.
>>: Your system is basically first order. First order logic?
>> Gabriel Dos Reis: Yeah.
>> Right.
>> Gabriel Dos Reis: But you can combine things. I will show you the formal version.
Yeah, I had the great part of it ->>: So is it even decidable?
>> Gabriel Dos Reis: Oh, yeah. Good question. Is it even decidable? So the system is
implemented so that I can experience [inaudible] but currently I do have restrictions like,
well, certain quantifiers cannot be nested and so that I can decide things.
And the other thing is it will be very interesting from software engineering point of view
what kind of restrictions. If you put certain restrictions on certain formulas, you got
formula, what kind of programming technique you can develop, what is supported, what
becomes too complicated.
Interestingly enough, [inaudible] is very first order and, as many things just, you know,
work beautifully. And of course I would like to test this in other fields and get an idea for
it, what actually [inaudible]. Yeah. But yeah. In full generality, [inaudible] that's fine.
Even in Haskell. Because have type classes and, again, two type-level computations and
it will terminate and so forth.
Okay. So good thing is that part of the algorithm that I developed for Liz were used by
Andrew Sutton who is a postdoc with me and [inaudible] on this new version of C++
concept. So we got the -- two weeks ago we got the ISO C++ standards committee
approve a technical -- they request for a technical specification, so we are going to say we
want this, but a committee want to have a -- some formal presentation first, and if we all
agree on it, then we just drop it into ->>: [inaudible] some sort of Axiom specification based on first order logic and C++?
>> Gabriel Dos Reis: Yes, that's the goal. That's the goal. And actually have another
student [inaudible] working on that with GCC.
>>: Good luck with that [inaudible].
>> Gabriel Dos Reis: Just have to keep asking. Get something. So last year I showed a
design and near implementation at CICM. And so the thing is that concept actually
elaborated into a set of proof obligations. When you define a generic function, you use
them as hypothesis so that you can check the body. And when you call a generic
function, you have to provide evidence that the [inaudible] was satisfied.
18
So that's essentially what the idea is about. So you can have a predicate like this, so
essentially a propositional formula, okay, you can ask that a set of operations exist. This
is a result through name lookup. And you can postulate axioms inside the concept
definition. This essentially requires the compiler to go up and see whether there is a
statement by user that certain property holds.
And then you have how to get rid of type tricks. Okay. Now, so essentially have the
quantifier, so asking you a question [inaudible], oh, so this is -- so the full
implementation is of course on this side of it because I want to experiment in several
directions, but here I'm showing essentially the part that I'm comfortable about.
So we have a constraint essentially. I have a quantified formula with the types, and
sometimes I want to be able to have some type deduced like in templates, you know, the
template parameters are usually deduced, so the question I have to say is I want the
deducible parts and expression, so this is just a simple expression. I can do a function
call, function definition, and then instantiated type.
I have a formulas and variables of course. Any program is just a definition of functions
or definition of concept. And when I'm doing type checking, I have to maintain all
declarations and I have to maintain the axioms, properties about entities. And of course
one thing that doesn't show here is that this is all since I have the codes between types, I
also need a congruence closure to keep track of when things become equal. Like when I
say codomain of F is the same as domain and I have to maintain that in all type checking.
So [inaudible] transition a little bit tricky, but it works. And it's actually earlier things I
did, and I was very happy about it and pretty cool.
So the implementation. So this specification here, I didn't say much about the monoid
type, but essentially because they are expressions and then becomes type by putting
restrictions on what actually it's like when you have dependent types, you have
to [inaudible] you actually evaluate to see what's going on and you put restrictions on the
structure. Yeah, Ben, you had ->>: Can you have infinite types, or is the issue compiler [inaudible]?
>> Gabriel Dos Reis: Yeah, so compiler does some evaluations like compile time.
>>: Okay.
>> Gabriel Dos Reis: Yes, but on the rules, for example, the rules essentially functions
that evaluate compile time. And we already have that in C++ 11 already. So I put that in
C++ 11 last time around. Yeah, it can have certain functions that are simple enough, and
actually it makes now completions go forward, but we already do that with templates.
This is a just better way, a simple way of doing the same thing.
I think in the '90s, there was -- a decidable type system was fashionable. But decidable
type system means a lot of things. Once something -- sorry. Once something is
undecidable, it can be undecidable for many reasons, right, because we don't know the
algorithm or sometimes certain computations are just gone forever. And if it puts a limit
on upper bound on how long you can spend time doing a computation, you get some
19
feedback. And you can increase. And, you know, even Haskell people now are very
okay with that idea. So I think it is okay now if undecidable means some computations
can go forever.
So doing type checking after [inaudible] closures for type equality. Just to give you an
idea about how I elaborate this. So if you check this source level definition, so this is
very pretty simple. So homogeneous is a -- here it is a concept constructor. So it has
type. It takes a type and there is a predicate on a type.
So when a predicate is unary, I can use it as a type. When I say F colon function actually
means F is a type such that the predicate function of F holds. Okay? So just short
notation. And define a concept. And in the body have that everything has to be -- has to
be positive and then elaborate this.
So I put some constraints on -- so question that I get very often, both implement in
position Y when I present this is what is the theory that we have for integers? Is it
everything? Can I test, you know, primality or that kind of stuff?
Okay, the problem is you'll say I can order integers and [inaudible] and have this built-in
notion of the integers are bounded. I can go more. I can do more, but I wanted
something, something pretty simple. And once you start writing, only the basic concept a
little bit complicated to get, but also get [inaudible] find it's pretty, pretty simple, right?
Here I'm just saying that codomain has to be the same as domain is the rule, so we get to
evaluate compile time, it get into this form and so on. So, again, equality constraint I
maintain them throughout type checking.
And one slide, something very interesting, is that when I say well, I have a notion of
monoid operation. So the idea is that you have this function, so certain types a semigroup
operation, and there also exists an element in its domain such that I have this relation
between F and that element.
And, by the way, I would like to refer to that element later on as neutral element. So
when I elaborate this, I get two things. I get the first definition of monoids, which is just
essentially that. I have -- I have a semigroup -- okay, it's alpha, so it's -- I have to be able
to defer alpha up here is inferrible from the argument F. Here is a bug. Sorry. Alpha
should be there. And then the actual function goes from F to -- from F to three concept.
And the requirement is just this. That I ought to be able to find this. And this is solvable
by just looking at database, query database and find.
Now, the other interesting is natural value. So this value is a function [inaudible] this is
the type families. But in a principled way. It is implicitly defined by the compiler and
the compiler knows exactly when defined because there is conditions on where it is.
And one other problem is we have with type families in Haskell is that most of the time
they're unprincipled. So when you design in an algorithm do type checking, you have to
imagine that you'll get institutions where the some Axiom just popped out of thin air.
No, in real programs, when people write real program, they don't pop out of air. They
have conditions. So if you take that ability out of the program, put it in a complier, you
20
can actually do type checking. Okay.
>>: [inaudible] predicate relating to the function and the neutral element, or do you
actually say somewhere, you know, that if you take an element from the group and you
apply F to that element in the neutral element you get back the ->> Gabriel Dos Reis: So for -- okay, substitute. [inaudible] but neutral element, no, it's
defined here, but I could have -- assume a neutral value GCD.
>>: But these F things are C++ functions, right?
>> Gabriel Dos Reis: Which one?
>>: The operation ->> Gabriel Dos Reis: Yeah, the function. Yes. So these are the points where I trust the
user.
>>: Well, but, you know, I mean, this thing could have like effects and ->> Gabriel Dos Reis: Oh, you're talking about -- the neutral value, no, the [inaudible]
function. They are side-effect free. The function that I'm talking about can have side
effect, that is fine. But these actions are pure functions.
>>: And so when you say F of X of E is equal to XX, why is it ->> Gabriel Dos Reis: So when I say this, I'm not running the program. I'm stating a
property that the compiler is free every time it is type checking and it sees this
expression. It is free to simplify to this. It is like these optimizations that compiled.
>>: And it's yours and it's total and it's F of X of I G ->> Gabriel Dos Reis: Yeah. Yeah. Yeah. So the point is this. It is already happening.
What I'm doing is to try to reduce the clutter. If I have the luxury of requiring everybody
to do proof, then it is okay. Okay so the question is this is designed how much you
require people to do and how much you tolerate as noise.
Okay.
If I can get away with -- if I can remove type traits and just have this, I'm very happy with
that. Because this is evil. The [inaudible] are evil. They're useful currently because we
don't have a better way of doing it, but if I can take that and represent this, that's fine.
We do this all the time. Even from the computer algebra point of view. [inaudible] so
usually people say, oh, 14-point numbers, they come from a field or whatever it is and
multiplying -- sorry, addition on 14 point is [inaudible]. Which is true. You can provide
examples and show [inaudible]. But if you know your input data, this is set. On that, that
assumption becomes true.
As a matter of fact, this is a real thing. One of the fastest modular algorithms working on
21
integers use 14-point double to present integers because it is relevant [inaudible] and you
get 53 bits.
And when you're running your program on your machine, the CPU is busy doing
arithmetic operations and Euclidean instruction. Why the 14-point units just sit there
looking at what's going on.
So if you move a little, show it to the [inaudible], well, it works. Because you know your
input sets. Of course that assumption is not true in the absolutely mathematical sense, but
you know your input sense, and that's what matters.
And this is okay library [inaudible]. It's one of the fastest library on modular semantics.
Pretty neat idea.
Okay. So where am I? Oh, okay. So you get, you know, this. And currently you have
some feedback. Andrew Sutton, who is a postdoc with me, implementing part of this,
part of the ideas, some of these scale of ideas into GCC. And we want this to get into the
concept of technical support. And Michael has been working with me for a year and a
half. He's working on axiomatic paths in GCC. So things are moving pretty good. I
hope that we'll get some [inaudible] paper soon. That's going to be hard [inaudible] PGI
is pretty good.
So for future work. I would like to have less segmentation faults in a published
[inaudible] and get [inaudible] pretty neat idea about how to use some of the constraints
for [inaudible] protocol. And so people like contract, programming by contact and that
kind of stuff, so I would like to understand much better [inaudible] integrating axioms,
things that you just assume for optimization purpose are things that you actually check at
runtime and what part of it.
You know, some of them you cannot check at runtime [inaudible] defined algorithm, for
example, assumes that the two iteratives you give, one is originally from the first and that
it essentially is running the algorithm. So you can check that system at runtime.
And of course I want for more account for this. And, yeah, can we trust the user all the
time? This is design point [inaudible] to look at. Okay. I'm ready to take questions.
>> Tom Ball: Thanks, Gabriel.
[applause].
>> Tom Ball: Any more questions? We asked quite a few. Okay. Great, well Gabby
will be here for the next two days. And thank you again very much for taking time to
come.
[applause.]
Download