 Я не как компьютер-санктист, и я не как теорист. С участием я альжебрейст, и я начинал с категории теории, когда я был студентом. И потом я spends most of my life writing code, и eventually gradually getting into understanding that this stuff is categorical. So more and more, and eventually I learned some topoth theory, so that's what this talk is based on. But I'm not going to introduce any topoth theory at all. No geometric morphisms, no adjoint functors. And maybe no monads. Just one monad. It's called maybe, so no big deal. Everybody knows it. And by the way, since it's not... So maybe one monad. Yeah, it will show up, yeah, as an extra. Also, since I'm not a computer scientist, the type theory that I'm showing is not the traditional kind of type theory, where you see those like sequence, those how they call it, turnpike symbols. No, I don't have them. Well, I mean, I kind of teach some kind of similar stuff, but it's not my area. So it will be all geometrical. And by the way, you probably heard several times today, heard today, that a type is a set. Well, this type theory is not based on sets. It's not based on, even on Boolean logic. So some logic is Boolean, some not. So I'll show you examples. Okay, for those of you that just came, this is the book that you're not supposed to have read. Yeah, again, wrong direction. So I will start with an example where Boolean logic doesn't work. Imagine we have a theory that describes something like your whatever, your program, your computer science, and we have some kind of like development of the theory. We have theory zero, zero that we had before, and then it turns into theory one that we have after. So we have like types from day zero, and then we have types from day one. And we have a function from types that we had yesterday to types that we have today, right? How about the sums? Well, we have these functions. If we have this function, we can like have functions defined on sums of types, on products, on these things maybe. Suppose like u is a subtype of t, so which means like yesterday we had u as a subtype of t, and today we have u as a subtype of t1. Now if we look at what's happening today, x1 could be of type t1, and it may be like, may belong to type u1, or may not. But it's a prototype. There may be a prototype in t0 that becomes x1 today, and that prototype could have been in u0, but maybe not. So we have two choices for x1, and we have actually three choices for x0. So the logic is ternary. The logic is binary or regular Boolean logic today. The x0 is either u0 or not, but yesterday it could have been u1 or not, but yesterday it could have been u0. It could have been not u0, but today it becomes u1. And here the, I don't see that example, but an example could be, remember Java, old Java, Java map, no, Java hash table, right? It wasn't implementing map like many, many years ago, and then suddenly it started implementing map. So that's how it is, things change, and types like something that wasn't our type, they suddenly become our type. So here's the picture, like the things that are in u0, the things that are in t0, and then with time they move into this. So in this case like u0 was always in u, u was always in u. This thing x was not in u, but then it becomes an instance of u, and t is never an instance of u. So we have three options here, two options here. We had something before and then we have something after. So this is the logic that can describe this kind of behavior. Now we have like false and true, and before we had false, true, and something in the middle that becomes true, but not always. So this is like an inspiring example of why we should consider cases that are not Boolean at all. But I was talking about theory, right? What is a theory? In a theory, as described in model theory, we have terms which could be variables or build out of functional symbols or something else. We have operations that may be relations, and we have axioms. Axioms describing the interrelationship between terms or between elements, between values. And those axioms can have these two quantifiers, universal and existential. A theory is geometric if it has existential quantifier. So I'm not going into model theory, I'm just reminding you or just telling you what the theory is. I don't need this computer. So like before we had this theory, meaning that we have types, and basically it's constituted some theory. So maybe your code is a theory, programming language is a theory. So what we have in this type theory? We have types, just names of things, just types. And we have functions between types. Node functions are not types. So types are like describing, maybe you can say sets, but we're not dealing with sets. Types are describing like some collections or like some waves of classifying objects into those collections. And functions map, well I wouldn't say map, they just like are between different types. So a function is from type 1 to type 2. And these are not, functions are not types. So there is no function type yet, I'll introduce it later. So think of them as something that is somewhere in hardware, or in the library, like you have Java, so you have those things that are implemented in JVM, or in your floating point machine, so it can multiply, it can calculate sine, cosine and logarithm. So functions in this specific type theory have equality, which means no, they're not functions that we know from sets, because like in our old traditional type theory, we cannot compare functions, there's too much to request, because we have to compare all the values, not in this case. Functions are given to us. So what we know about functions here? So they have equality, we can say that two functions are equal. Functions can compose, so we have a function from A to B, and we have a function from B to C. There is a function that is a composition of these two from A to C. For each object, for each type, meaning I'll be mixing objects and types, because in category theory they are objects, they are called objects. So identity is given for each type, for each object, and identity meaning that identity composed with any function, keep that function, left composition or right composition. Yeah, we have a category. So we have a category, and let's see what else do we need. I define a subtype. A subtype in this specific type theory is any monomorphism. And what's a monomorphism? If we have like a given, so we have two types, A and B. H is a subtype, which means it's an inclusion of A to B. And on which occasion can it be an inclusion or subtype? If we have two functions f and g, from some C to A. And if f followed by h is equal to g followed by h, then f is equal to g. So it doesn't glue distinct functions. So that's why, like in set theory, yeah, it's a regular injection, right? And any notion of monomorphism amounts to this definition. So I'll be denoting them like this, so that the standard notion for monomorphism. But there are the subtypes. And again, it's like it's interesting. So how come, like, say, take real numbers and take integers, right? So we can inject integers into real numbers in many different ways, right? So we have many different subtypes. One subtype is odds, the other subtype is events. The other is just all integers. So it depends on how we inject them. So this is the definition of subtype, and I'll be using it. And of course, it's a little bit like, it's a little bit different from you have in sets. So now we throw in one more specific type, a unique type called unit. It's also called top, it's also denoted as one. And what's so special about it? Why is it called terminal? There is exactly one function from any other type to this key. So you encounter units like in almost every language. So there's nothing new here, right? But now take a look at these functions from unit to A. These functions are called points in categories, or they're also called instances of type A. So T is a type, and since there is just one function from T to T, because one function from any A, T has just one instance. In Scala it's denoted as parenthesis. But I'm not talking about Scala, so it's just an illustration. So, see, having this specific terminal type, having unit, we can define instances. And an instance is also, see, it's a function, right? So an instance is a type. Now, how do we, having functions, how do we apply functions to instances? It's a composition. We have X, and X is an instance, which, an instance of type A, meaning it's a function from unit to A, right? So compute this X with any function F, this composition, right? So you have F of X. So this composition is an instance. And what is it? Because X is from unit to A, and F is from A to B. So the composition is from unit to B, which is exactly, exactly an instance of B, a point in B. So this thing allows us to talk about functions of something that maps, unit maps points, maps instances to other instances. The thing is generally speaking in, like, if we throw out set theory, that's not, that's not how a function can be defined. So two functions can be the same, can be equal as mappings from instances on one type to another, but that's not enough. They still can be different. I'm not going to give an example of it, but that's kind of, kind of obvious, because functions are not defined by their compositions with units. There may be something more. Okay, so we had unit, that is terminal type, now product type. It's known as pair type, right? Like A, B. And, you know, it's like traditionally written like this in Scala for instance. So it's called product type, and in this theory it's defined like this universal property. So given two types, this type always exists. It has two functions, two projection functions. So the first one, of course, maps an A, A B to A, and the other one maps A B to B. So two projection functions and every two functions, like from an arbitrary C to A, one to A and another to B, are in one to one correspondence with functions from C to this product. So I can draw a picture like this. A here, B here. So having these two things, this is given to us. If we have it like F here, G here, so we can say FG. And of course if we have this function, so we can composite with projections and we have this. So this composition, so they are in one to one correspondence, meaning that given this composite with projection, you have this function, composite with projection you have this function. So they are in one to one correspondence. And if, for instance, you have C as unit, you have a point, right? You have an instance of A cross B. So that instance is exactly a pair. So that's all about product types. No, not all. Yeah, yeah, so that's all about product types. An instance of A cross B is a pair. And it's equivalent to having a pair of instances. Also, yes, keep my mind. Now, one interesting case of such a product. Let's have a product of A with itself. And we have identity function from A to A here from A to A here. It gives us a function from A to its product with itself, right? This function is called diagonal. And of course, if you look into what it gives for elements, it gives a pair A with itself. This picture, I'll be using it like regularly. It means this is in one to one correspondence with this. So having a pair of identities, we have a diagonal. Given a diagonal, we have these two identities. All because diagonal followed by projection is an identity, right? And also we can see, we can check, I'm not doing it, that this will be a monomorphism always, which makes A as a subtype of this pair, of its square. Which also means that, say, if you are familiar with linear logic, where this diagonal doesn't have to exist. So the logic is not linear. In linear logic, if we have like one dollar, and do this diagonal, we would have a pair of dollars. So this doesn't work in linear logic, but here we can have it. Meaning, probably diagonal is not good for dollars. So that's not enough. We are introducing the exponential type. Now we use product on the left side of a function. So given a function like from A cross B to C, meaning like if you think in defining functions in instances, so we have an instance of A and an instance of B and produce something in C. So having a function from A cross B is the same as having a function into this type, which is called C to the power of B. It's called exponential type. Or you can see, it's like a type of function from B to C. So the operation is carrying, right? So we have it the same as having a special case. It's a special case when the left component of product is unit. By properties of unit, there is just one projection to unit. So T cross B, unit cross B, it's more or less the same as B. So any function from B to anything is equivalent to having a function from this to this, same thing. I mean strictly speaking it's isomorphic, but for types we don't care. Isomorphic is a case. So having a function from B to C gives us an instance of this exponential type. A point, an instance in C to the power of B, which means all functions are in one-to-one correspondence with points in C to the power of B. But they may be more than just points in objects. So we can talk about, so functions being like provided with exponential types for every pair gives us the opportunity to have, to map every function into a point. But points is not all that an object can have. So now just having these like simple things, we can build a lot of stuff that is like becoming kind of a part of computer science. So take this identity function from B to the power of A to B to the power of A, right? As we saw before, these things are in one-to-one correspondence, right? So let's do this, flip it over, and we'll have a function from A cross B to the power of A to B, which means what, like in elements, element-wise. Take an instance of A and a function from A to B, which we already said is an instance of B to the power of A. Given these two, evaluation gives us the application of a function from here to the element to the instance of A. So it's also known as flip ID in Haskell. It's not known in Scala, probably, but anyway. So that's what it is, or in Scala it can be a reason like this. We take identity. Is it here? No. Yeah, it's more than that, anyway. But it produces... Oh yeah, it's identity, yeah. On B to the power of A, yeah, on this type. And on carrier, and we have evaluation. So evolve function. So that's pretty simple, right? We build this something from out of nothing. Application of function. That's not enough because we need logic. And logic here will be a little bit weird. So we reserve one special instance. No, no, we reserve this logical type. Type of logical values. In set theory, it's true and false, just a set consisting of true and false. Generally speaking, it can have more. It doesn't have to consist of just points or just instances. So we introduce this type. It's traditionally denoted as omega. And there is a special instance called true. Instance meaning like a point. So it's mapping from unit to this omega, right? So, okay, given this logical type and a special instance true. And every function from any type A to omega is called predicate. Well, because this is a type of logical values. So, and we introduce this axiom. Predicates, functions from A to omega, are in one-to-one correspondence with subtypes of A. In set theory, it's denoted like this. A1, this subtype, is the set of all elements of A such that this predicate is true. So it's how they call it, like comprehension axiom, suppression axiom, whatever. So, generally speaking, in category theory, it's denoted as pullback. A1 is the biggest, the closest type that we're going this way. Included, injected into A and then applied predicate. It's the same as like going through unit. The mapping to unit is unique, so we ignore it. And then we get true. So it consists exactly of all those things in A that are true when the predicate is applied. So we have this one-to-one correspondence between subtypes and predicates. Which makes it pretty powerful and which makes set theory pretty powerful. Because we can logically describe things and you can define subobject by like logical expressions. Subtypes, subsets, except that, hmm, what expressions? Do we see any expressions here? Not yet, right? We don't even see false, we only have true. So in next slides, I'll be working on introducing like these things, logical connectives. But first, let's start with power type. So we have this omega and already have these exponential types. So let's do it omega to the power away. So what is the point, what is the instance of omega to the power away? It's the same as the function from A to omega, right? Well, because it's like for that mapping. And what's a function from A to omega, that is predicates, right? They're in one-to-one correspondence with subtypes of A. So which means having an instance of omega to the power of A is the same as having a subtype. So for a type A, we have a type of all these subtypes. Which is pretty like powerful requirement, right? So we don't have this say in SQL or like in any probably other programming language. We can also express it like this. We have a predicate from A to omega. And A classified by this predicate is like this. So an instance of omega to the power away represents a subtype. But we are not saying all these things are given right away. Let's go back to diagonal. So if we have a diagonal, remember I said the diagonal makes A a subtype of its own square. And it maps an instance of A to a pair A. So what does it, what predicate would classify it? The stuff that the predicate would say that, yeah, let me write it. If you have A maps to A, the predicate would map A A prime to A equals A prime. Well, see why? Only things where left and right part are the same and map to true. So this thing is equivalent to this. So by having diagonal, we introduce this predicate that classifies this diagonal and this predicate is equal. So equality is given us by the other axioms just because we have diagonal and because we have predicate for every subtype. So now let's do tricks, like given an equality, right? So it's from A cross A to omega, the predicate. Flip A over there and we get what? Singleton. How come? We have this predicate, right? So it gives us A maps to the set of such A primes that A prime equals A. Which is a Singleton, right? Which is a set, if we are talking about sets. It consists of just A. So we have this Singleton function. Oh, this we already saw, right? So given an identity, we have this flip ID evaluation. But in this specific case, why is it denoted like this? See what the trick is. Given an A and some like subtype A prime, its map to A belongs to A prime. Why is it? Because if we go back, we'll see that the set is mapped to itself, right? So here only elements of that set are mapped to true. So Singleton is an equality carried and equality, remember equality was also carried, right? No, equality is a classifier for diagonal. Okay, so we can define more things like given two functions from type A to type B. We can define an equalizer of these two functions, e is the closest to A. The set of all, if I'm talking in the terms of sets, but the word set is like kind of artificial. It's a part of the language, but we are not talking about sets as in set theory. So the set of all these such As on which F of A is equals to G on A. And it's defined by this universal property, but let's keep that. And in SQL, it's expressed like in this way. So given a type A, okay, like if tables are types, right? Given fields F and G, suppose they return like the same type, some B. We select all those things from A, where A.F equals to A.B. That's a typical thing, right? Now, how about having two types, two tables, select something from A and B. That's called pullback type. So if we have A and B and we select from A and B all such things, where F on A equals to G on B, we have this thing. It's called pullback. That's the closest collection of pairs A, B, such that F on A equals to G on B. So this is a type again. So we can introduce this type. So we have like tons of types, like in set theory basically. So empty type. At last, I'm trying to introduce some logic and some false. We can build empty type, which is known like in programming languages as nothing, or bottom type. It will be unique. It has a unique inclusion injection into any other type. And we can build it using these tricks. So basically, it's like all those truth values that are not changed by any other function. So because if you have like more than one truth value, so there is a swap. So is there such a value? No. Which means it doesn't have any element. So there is, it's empty, meaning that it's empty. So there is, it's empty, meaning that there's no instance of it. So this is a bottom type, and it can be like informally described like this. The set of all such axes from omega, that they fix points for any function. And okay, it gives us an empty type. Now, if we have an empty type, now we can build false. False is such an instance of omega, that it classifies empty subtype of unit. So it classifies nothing. So what's the feature of false, right? So we can rewrite it like this. Bottom is a set of such axes that false. Especially this here, I take like axes from omega, but it doesn't matter, because we can take axes from anywhere. There will be isomorphic. Why? Because there is a unique injection of such an empty, such a bottom set, such a bottom into any other bottom. So they're all kind of the same. So union type. We can build a union type here. The thing is I'm not going to show how it's being built, but basically it's built out of products and singletons. So take those specific pairs that have only just one thing there. So that's how it is. So first, the joint union is the smallest type where A and B are subtypes. That's a natural definition of the joint union, right? And it can be built here. So having all the things now, we can build logic. So first, let's start with conjunction. Given omega cross omega, conjunction is a classifier of pair true and true. Sounds logical, right? So given true and true. What would be the predicate that classifies both? It's a conjunction, right? We can say that. So that's the classification. That's how conjunction is defined by just classifying a pair of truth. Disjunction is a little bit trickier. So it's either this or this. So we are classifying the union. So if this is true, if the left pair contains true, good. We have this true. If the right pair contains true, it's good too. So that's like an opportunistic operation. And it classifies this union of omega with omega. Which is a part of omega cross omega. Well, because we have these two things. One is defined on the left side through an identity. And the other one is defined on the right side through an identity. So we have conjunction and disjunction, right? And we already had true and false. We can go ahead and check that conjunction is disjunction. Associative, we have those distribution laws. And we can also, before we go to negation, let's define implication. So what does it mean? Implication classifies, implication takes a pair of truth. Two logical values, right? And classify them into true, if what? If the first component is weaker than the second, right? What is implication of A and B? A is weaker than B. So what does it mean, weaker? We are defining here sub-type of omega cross omega. Consistent of those weaker things. And what is weaker? Meaning that the first projection is the same as conjunction. That is A cross B equals to what? First projection, which is A. So implication, more or less informally, but actually formally, is defined like this. An implication from A to B is defined as A and B equals to A. This is kind of close to what you see in Boolean logic, except that it's not defined through negation. On the contrary, negation is defined through implication. Negation is defined as negation of X is when X implies false. So you can easily prove that triple negation is the same as negation, but double negation is not necessarily identity. And I'll show you the example. We can define quantifiers. So we have enough. The type theory is powerful enough and the logic is powerful enough to define quantifiers. Like universal quantifiers. What it does? It takes functions from A to omega, that is predicates, right? And it gives us a value in omega. So given a predicate, it tells, is this predicate true on all A? And what it does is it just classifies true. So bottom is the same or is isomorphic to bottom to the power of A. Remember we had just one function from A to bottom, right? So true to the power of A is a function from bottom to the power of A to omega to the power of A. It's an instance of omega to the power of A. And what this instance is? Is it bottom? It should have been top, right? So read it as top. So what is this? It's equivalent to having a function from A to omega, right? So that's our predicate. And classifying this predicate in omega, classifying this, we have a function from this to this. It maps a predicate to true only if it's true on every A. On the whole A, not again, on the whole of A. We can also build an existential quantifier, but let's keep it like a bigger thing. So as a bonus here, maybe monad. We can build it here. So what's this? What do we see here? Given type X and subtype X1, and a function from a subtype X1 to Y. So this is called partial function from A to Y. And partial functions are officially denoted. There is an iso-standard. Officially denoted like this. Arrow with cross. I forgot the number of that standard, but that's z-notation. So z-notation is standardized. Actually, this standardized the whole set theory. And I wondered, if this standardized this intermediate power between continuum and countable or not. I really don't know. That's Europe. So having this partial function, we can, in this type theory, we can extend Y to what is known as maybe. And give a function and having this maybe as the smallest one. In set theory, just plus one. So anything that is not coming from X1 goes to that additional point. Nothing to none. But generally speaking, it may be more sophisticated. So it can be built out of omega to the power of X. Now let's see how it maps into our example that we had before. So suppose we have sets and we have two moments of time like before and after. So two is that, okay, it's called, this category is called set to the power of two. So it's A. So the types are defined here as this. Yesterday type A0, today type A1 and a function that maps from yesterday to today. Which also mean that we don't drop anything. So which was like any element, any instance that was of type A0 is somewhere here. So we're not losing the other function. So it's different from like having two distinct and independent sets yesterday and today. So what's a subtype? A subtype is, it was subtype yesterday and it's subtype today. And this, like the things are compatible. So since we have a function, so the mappings are compatible. So for instance, if we have an instance yesterday and an instance in today, so the whole instance X, which consists of X0 and X1, is given like this. It's an instance of A. See the picture. So in that logic, I denote it as this, like this is the yesterday part, this is today part. What's unit, that top? It's just one point yesterday and one point today. How many subtypes does this unit have? We have the first subtype itself, the second subtype empty, nothing here, nothing there. But also we have this half unit. We had nothing here, but we have something here. An empty set here and a point here. So here's an example of a type that we can call point and a half. So we have this point and half point, just this. So that's what we had yesterday and that's what we have today. Yesterday we have two instances, now we have, no, yesterday we had one instance. Today we have two instances. This example shows that points may not be enough to define objects, define types. There's more than just full instances. We can redraw all this, like this picture. So describe things like, this is where our instance always was. This is where we'll be tomorrow and this is where we'll never be. Like again, hash table, what's subtype of map? No, not, wasn't, was not a subtype of map, was not implementing map before like Java 1.4. But then later hash table was retrofitted to implement map. So hash table is some, no, map is somewhere here. So suppose, like, I showed conjunction and disjunction. Conjunction and disjunction and of course give us the opportunity to build intersection. And all these things, like unions. So suppose we have this, I don't know how readable it is. So we have like one subtype here. This is like, this is where we always were. This is where we get today. And this is the stuff that never was an instance of our subtype. So this is subtype A and this is subtype B. How do we build an intersection? An intersection is something that consists of these three kind of aspects. This part always belongs to both A and B, right? So this part. This part is the thing that will be eventually there, like it's there today. On the left, it was always there, but on the right it was not there, so we have this. And the same here. And the outside is the thing that never belongs to both. So that's how intersection is being built. And union, it's like it's the opposite. So take this two and we take a union. So things, instances here, they belong to either that or that. Even if some of them don't belong to both all the time. Instances here, instances here, they belong to either of them, to one of them. Not either, I'm not saying either. So one of these subtypes today, but not yesterday. And these things never belong, so they were outside. So that's how it looks like in this. And also negation. It shows that negation, double negation is not the same as identity. So have this like, this is a subtype. Where this is instances here, they always belong. Instances here, they belong only today, but not yesterday. And the instances here, they never belong. So take a negation. Now, these guys here, they belong to negation. These guys here, they belong to... No, these guys here, they don't belong to negation. We'll never belong to negation. Yeah. So take negation again. And we have this. See, we have this bullet. Either true or false. So things that became... Things that got our type only today, after double negation, they become ours. And things that are outside, they will never be ours. So that's how it is. Double negation, if you take double negation, it returns only true or false. So that's it with this short introduction. These are the things to read with a lot of inspiring. This is the standard. Razetta Stone covered these issues. Well, it never mentions intuitionistic logic, or maybe it does. But it shows like a very, very close interpretation of this. This is a big book that now costs like 18 bucks or 22 bucks. So it makes sense to buy and read. And the other things are like kind of inspiring readings. And I believe it's published on Github. So, no, I mean, wow, there was... Okay. Редактор субтитров Н.Закомолдина