 Dwi'n rhaid i'r ffrwng y Plutuscore. Fe yma, y Plutuscore, gilydd y hwnnw i'r targetau teulu o plutus Tx. Plutuscore yn system F. Mae'r apology o gywlai ddefnyddio yn amhwymoedd yn iawn. Mynd i'n digwydd prygu ar rai gyda sormy mewn, a mae'n gwneud o ddechrau ond. Plutuscore ymlaen i'r drynu yn bwysig a'i ddefnyddio'r bwysig bod mae'n cyfrifwyrd. OK, so you might wonder why should we formalise this? Well we want to move from pen and paper specifications to having machine checked specifications and machine checked proofs, so that's so that we can have higher assurance. So quite often when you have a programming language you might, if you are lucky you might get the grammar in the appendix in the back of the book. And there are some programming languages where they have a formal specification like standard ML. That's a pen and paper specification essentially. You can't get away with that these days. I think you really need to have machine check proofs. Another benefit to doing this is that we can write an extremely precise specific implementation to use for testing. So instead of saying for example that you have an evaluator where we just say we're only ever going to run this on well type terms because we don't really know what it does if you give it a badly type term. Ond we can actually guarantee that we have an evaluator that will only ever run on the world title term by using the typing in the same way as if you write the function which expects the list. You can't give it a string or something or an integer. So an this approach has been used quite a bit for some quite impressive to produce and quite impressive results. So there's the four color theorem, which has been formalized in Coq, that's a theorem that you can't check by hand because it has so many cases. There is the very impressive concert C compiler which is an industry standard C compiler. There is also the SE-L4 OS kernel which was used very successfully in the Hackham's DARPA project, and there is KKML which is a formalised functional language that has been formalised in Isabel. You might also wonder why do we want to use Edward Magdal. Edward Magdal is a really nice interactive theme, improving like pok. It is a really nice project language As raman has just demonstrated, is a nice dependently type language. Haskell is acquiring some of these capabilities, some of those ideas have come from Agde. Much in the same ways Cok, Prussian programmes are really the same thing, because they are based on the same underlying type of theory. But differently to Coc, proving and programming are the same in Agde – the same activity. I think this makes it more readable and understandable to programmers. Also, I think it's especially suitable for working with programming language semantics because it has a really rich system for working with data types where you can be, it has a very expressive system, you can be very precise. A very nice feature is that it interoperates very nicely with Haskell so you can actually use real Haskell libraries. It has a foreign y dweud o'r ffordd, i'r cyfnodd ychydig i Haskell. A dyna fydd yn gweld yn yr adeilad. Mae'r adeiladau fyddiad yn agdur yn y twyd i'r gweithredu. Felly, mae'n gweithio'n gweithio'n fwyaf i'r ardal, a bod yna'n gwybod i'r amser. Felly, mae'n gweithio'n gweithredu o'r gweithredu ar Eishir. ac mae'n gweithredu ar gyfer y cyfrifredig o'r enghraeg a'r enghraeg. Mae'n gwybod i ddod o fwyod o'r ffrifredig. Ond ym mhobb yn ddceltydd. Ac mae'n dywedd iddyn nhw'r lleiddau ar gyfer cael eu cael ei bod yn gweithio'r llywiau, diadwyd perioedwiaid a'r effeithio'r maen nhw'n hoffiessau'r llywiau a'i fod yn beth o'i cyhoedd ychydig sy'n meddwl ar gael y greaddau. Yn y rhwng hon i'r gwanfod eraill i'r proj hypellio'r llywiau a'r prodiau ar gyfer IOHK, neu ddisgrifetau'r prosesiol i'r meddyl Isobel, oedd ychydig o falm ddau cyfrifiadau, gyda'r symud hwn o wgwmnigydd ymddangos, a oedd y ffwrdd os yw fforddysgwyl yn Coc. Felly wrth gyd, rydyn nhw'n gobeithio y dyfodill i ddiwethaf yw wneud, yw'r oeddoedd ichi'r gefychol a wnaeth ganddo i'r mynd i'r felch. Rydyn nhw'n ganddo i'r rhaglen y cyntax i'r syniad a'r wylchol. Rydyn nhw'n ganddo i'r hyforddiad cyffredig ac nhw'n cy fry'r syniad yn bryd. Ie, chwy fydda i'r dynamos, mae gennym ni'n fyrdd i'r ffordd y ffordd y Llywodraeth Llywodraeth yn ei angen yn ysgolod y Sam's Creson, mae'n gynhyrchu cyffredin a gynhyrchu cyffredin, yna ymwneud cyffredin a gynhyrchu cyffredin. Mae gennym ni wedi'u gweithio'r prysgau o'r gyferrwynt o'r system Ffyn yw ysgolodau. Felly, mae'n gobl ar gweithio'r gweithio'r gweithio, can use this to hook up an evaluated by iterating that. We can compile to Haskell for testing and this gives you a sort of, you can't exactly replace the Haskell implementation because it's really, you can generate a sort of prototype Haskell programme that is almost certain to not be as efficient and not as reliable and the Haskell code won't be readable but it's just a compilation target. So to an extend this doesn't really matter. And of course you could go on and do lots more things if you want to have a deeper formalisation. Right, so I'm going to look in detail at the systemf fragment of please call which is in a sense the core. I will mention the extensions afterwards but let's just look at systemf omega. So it has three levels. So it has kinds which are just base kind and function kind so that's exactly like Fritz said it's the, these are just the types of simply type lambda calculus. And on the type level we have lambda application and variables so we have the terms of simply type lambda calculus on the type level and then we have the for all that's from the polymorphic bit from systemf and we have function type. So you can think that simply type lambda calculus fits into this at the kind and type level. And then at terms we have regular lambda application. We also have big lambda so you can abstract over a type and we have instantiation which is like, which is really the application but for a type variable and we have variables. So you can think again system, sorry, you can think that lambda calculus fits in at this level so there's sort of two overlapping copies of lambda calculus here. And it's important to bear in mind so that's my executive summary that we do have, we have bindings in types and we have computation types but we don't have any dependency. So you don't have terms appearing in types so the sort of information goes down. So types can refer to kinds, terms can refer to types and kinds but there's no, there's no sort of circularity which makes it not as hard as dependent types. Okay, so let's have a look at the syntax of this in detail and so this is both the syntax and the type system so this is another kind of version of propositions as types. So if you think about it in terms of syntax what this means is it's impossible to construct a term which is ill typed so this makes it very, very precise. You can also just think of this as a straightforward formalisation of the type system and of course we are interested in properties of the type system. So let's look at it in detail so we have kinds at the top, I can use my pointer, so we have kinds, this is the kind of types and then we have function kind, that's the f omega part right. And then here we have types and this is written in a way to remind us of the type system so we have variables and we have lambda and here you can see that the result is in context phi but the body has an additional variable. So that gives us guarantees that things are in scope. And here with application you can see the guarantee of well typedness because you have something or well kindedness I guess. So here you have something of function kind, something of the appropriate arguments and then you get something of the appropriate result and you cannot fit together things that are not supposed to be fitted together. And here we have the binder rule or pi and that the body of that is abstracted over an additional variable. And you notice that pi constructs something of kind star and so does the arrow type so in a way these are like the two base types. And then in the syntax, so this is indexed over a context and a type and here we have variables, we have application, variables, lambda, application, we have big lambda and instantiation and then we have this rather annoying thing called the conversion rule and this is a typing rule and we need this because we've got computation at types because you might have a type which is really the same, somebody hasn't reduced a bit of it yet, you can have a beta reduction in a type and we want to make sure those things are the same otherwise the system is not going to work. So that actually appears as a syntactic constructor if you work in this way. I would say this is the standard presentation of system F omega with the conversion rule. Right, so then we need some functionality, we've written the type system down. We need to implement substitution for types because we need it and we also have to prove stuff about a substitution, we want to know it's correct and my favourite way of doing this is because you only have to prove there are just three properties is just that you prove this forms a relative monad. So substitution is bind and the variable constructor is return. So I think that's a nice way of presenting it and then the funny thing is we actually need to use both of those things, the operations on types and the proofs when we define substitution on terms and that means that our proofs have to compute. We can't postulate something or use a principle which is okay but doesn't have any computational behaviour. So we really need that the proofs compute so that's an essential property. And Colin McBride taught me a nice trick which allowed me to do this because we represent substitutions as functions and we're still able to avoid using extensionality. Okay, so that's that, that's fine. However we run into a halt here because we can't prove progress and preservation for this system and that sort of stops us in our tracks and that's because we don't really know what to do with this conversion rule because we need to kind of somehow push it through the terms and it's not clear at all how to do that. But there is a solution. We can normalise the types. So here is a presentation of the type system again with normalised types. So this has got rid of all the beta reductions. And because we're using the broyn indices for variables that means that normal forms are really unique. So if you normalise two things that are beta-e to equal, if you normalise them then if they're beta-e to equal in the first place you literally get identical normal forms. So this gives us sort of unique representatives of types. And so the way we do that is we define normal forms in neutral terms. So a normal form is something that's sort of in constructive form so it's either a lambda or a pi or an arrow. And a neutral term is a stuck term. In particular it's something where the thing in the function position in an application is a variable or some other stacked sequence of things applied to a variable. So we have neutral terms which are either variables or they're an application where the thing in the function position is neutral. And then we have the normal forms which are in constructive form basically or neutral term. So we replace types with normal types and that means that we're indexing by normal types in the type of terms. And it's great because we can get rid of the conversion rule all together. It disappears because it means that this A and B, they're not just B to equal, they're actually identical so we don't need the rule at all. So yes, yes. In fact that's an important point I'll come to in a minute. Okay so then so what do we want to do? Well we want to implement substitution right but now, so that's fine we can do that except we need to make sure that our substitution operation is normalising. And there are various ways you can do that. You can use for editory substitutions, I've used MBE and accommodation of MBE and substitutions. And so that works, that's fine except so now the sort of you actually need to have proofs about normalisation potentially in your proofs about substitution but that all works. And then we can do progress and progress says so we've got a term, a well-typed term, then either it's already a value so we can't compute it any further or it can make a step to another term. And we don't actually need to even talk about preservation which is saying that when we do reduction the type is the same as the one we started with because this is guaranteed because we're in a dependently type language. So when we specify reduction we just say that it goes from one term in a particular type to another term in the same type so that's just built in. And a nice thing that I learnt from Phil is that we can iterate this and write an evaluator. So we just say if you have a well-typed term and you're allowing evaluation of a certain number of reduction steps then we're either going to get a new term that it reduces to in zero or more steps and we might get a value out as well. And we won't get a value if we run out of gas basically if we didn't allow enough steps. Well, you can do this if you didn't want to bother with termination but let me try to be clear. So what I meant was we can do this, I have done it for the full system but here I'm talking about the smaller system so I'm maybe being overly pedantic but it works, it is done for the full system. At the type level the type language is normalising. The term language yes because you just have this constructor mu and it doesn't do anything, it's just a binder. Do you then really capture the full conversion theory? I believe so, so there's an isomorphism but we have wrap and unwrap constructors at the term level so there's no weird computation happening at the type level. So basically this is the key thing is that you're not treating types de facto recursively as you do with other types. We're using iso recursion rather than iso recursion because they're all very different types. Yes, yes, so we have witnesses of the isomorphism, we're not doing equi recursive. If we have time I'll have a slide somewhere at the end. Anyway, so we can come back to that. I've solved the problem, so I had a type system I couldn't work with, now I've got another type system that I can work with. So what should I do? Should I just chuck away the first one and forget about it and say it was wrong and just work with the... Kenneth is nodding enthusiastically and then just work with the new one? Well, I think the original one is kind of like the standard textbook definition of what system f omega is supposed to be. So instead we should prove how they're related and the way we should do that is you can think of the second type system as like being the type checker and we are trying to prove that our type checker, we should prove it sound and complete. So soundness says if our type checker, which is this normal version, says yes, then the original system should say yes as well. And here we have this weird situation where so we've got gamma and out and A are normal things. So gamma is a context of normal stuff and we then have to write a function that embeds those normal things back into the syntax to be able to talk about this syntactic judgment. So we have to embed both of those. But that's just saying if our type checker says yes, then the real system should say yes. Otherwise our type checker is doing something weird and completeness says that if something is really well typed then our type checker should say yes as well. Otherwise it's missing some stuff. And the way this is specified, you're allowed a bit of wiggle room. So you don't have to, if the original type system says something has a particular type, you don't have to say that my new type checker says it has the same type. You just have to say it has A type and that they are B2E2Eq and they would normalise to the same thing. So you have that extra bit of wiggle room. So interestingly, if we're thinking about type systems, these are kind of theorems we would need to prove. But if we think about programmes, then they're sort of interesting programmes. And one of them does exactly what Sam just mentioned, which is the second one takes a term and normalises its type. So that's just kind of a useful thing you might want to do, particularly if you want to store something with a normalised type on the block check. And the other one takes a normalised thing and then just converts it back to an ordinary thing. And this extra property, that A is B2E2Eq to its normal form, is a property or is the soundness property from a standard normalisation proof. And then there's a kind of a, I really like this one line of this function. So the way it normalises a type, so here we've got a conversion. So we've got sort of A is of type A, this says that A is equal to B and the whole thing is of type B. So then we need to produce something of the type normalised B and we've got something of type normalised A. So that's the problem, that doesn't seem to work. But we've proved that if A is B2E2Eq to B, then their normal forms are identical. And we've got exactly, this P is exactly of this form. So we can apply this thing and rewrite the goal and then magically A is exactly of the right type to put into the goal. So this proof or this program just goes through by collapsing all the conversions until there are none left. So I think that's really nice. And if we just take a step back for a minute and think, what do we just do? Well I would argue that this is IOHK's formal methods approach in miniature. It's like one step of it because we started with this standard definition from a textbook and we've converted it to a version that actually works. And it's algorithmic, we can implement a type checker using it. And we've proved that this step was valid with our soundness and completeness proofs. So I'm short of time. So that was, we just had a kind of a deep dive into the core of Plutus core, which is system F omega. So now let's just think about what we need to add. Well we need to add recursive types and we need to add built-in types which are size integers, size byte strings, and we need to add some other operations that work with these byte strings, etc. So let me say a little bit about system. This is maybe not going to satisfy Fritz because I'm not going to go into enough detail. But we need these iso recursive types as you've heard a lot about because we want to translate data types. They also give us general recursion because you can type the Y combinator or variations on it like the Z combinator using these types. And you can, Scott Numer was an example of that and does that mean that type evaluation might not terminate? It doesn't because it's still normalising. And then so how do we work with built-ins? So for integers, they're size, and you can just think of that as like a pair of... Ah, I edited my slide just before my talk and I've introduced a mistake. So you have an integer and it has a size and I represent that as being basically a pair of that integer, not a natural number, and a proof that it's within the bounds. And the bounds are specified in bytes and this is a calculation to give that it's within the maximum minimum value. And then with byte strings, the size is the length. So when you have a byte string, we also have a proof that it's shorter than a particular length. And when we implement operations on those things, we check the bounds. So when you add two numbers together, the type of addition says that the two things you take have to be of the same size and the result has to be of the same size. And we check if the result is within the bounds. And if it's not, then this operation fails. And if you want to produce something bigger, you have to explicitly resize it using a resizing operation. And then when we compile these, we can compile these built-in things to the same Agda library operations that the proper implementation uses. It just says error. So the whole thing computes to an error. So errors bubble up to the top. So I want to say a little bit, I'm already over time, but I want to say a little bit about compilation to Agda, sorry, compilation to Haskell. So you can take any Agda program and you can try to compile it to Haskell. And it can use Haskell libraries when compiled. And so I want to kind of like with integers, I want them to be compiled to Haskell integers. But there's a question about how do you represent this in Agda? And there are sort of two ways and I've actually used both of them. So you can model them in Agda and that means that you can then use them sort of in Agda. You can write proofs, you can run programs in Agda like adding integers, et cetera. So you can model them. But you don't really have a guarantee that when the thing you modeled, when you compile it, when you compile your version of addition to the Haskell version, that the Haskell version is the same. So there's also a sort of conceptual thing about what are we actually trying to formalise. Are we trying to formalise as much as possible? Or are we trying to formalise Plutus Core and how it interacts with the real world? So this is kind of an approach where you try to model everything. So I'm experimenting with that. Whereas for byte strings, I've represented them opaqually, which is we just say there's a set which is basically a type in Agda called byte string. It has a length operation and, of course, it has some other operations on it as well. And when we compile byte string, it gets compiled to the Haskell byte string from the byte string library. And when we compile the length function, it gets compiled to the length function. And we can still, even though we're doing it opaqually, it still means that we can think about what properties of these things we need to have and we can write them down. So that's already helpful. And we can also just give us some clarity about what we're expecting from this interface to the real world. I would like to connect proving and testing in the following way. So I would like the Plutus Core built-in operations, I would like to check that they have the right properties. For example, for any byte string and any integer, if you take something off the front of that byte string and then you drop the same amount of things off the front and then you stick those things together, you should get back the original byte string. So that property should hold for Plutus byte strings. We also expect that it would hold for the Haskell implementation and that's something you can actually just quick check in with the Haskell implementation. So I think this is a nice way of connecting proving and testing. Right, I've got to the end of my talk. So I'd say the contributions are that we've got a machine-checked version of the meta-theory for intrinsically typed and intrinsically sized Plutus Core. We can compile to Haskell, execute small examples. And this is a nice thing because this means we can sort of compare the real implementation. We can test the real implementation against this version. And I'm extremely happy with my MBE proof because it had to be really good because it gets used at, it actually had to run it. So I'm very happy with that and I'd maybe like to try to publish that on its own. And I should also, I'd like to acknowledge that two people, so James McKinnon was extremely helpful talking to me about pure type systems where some of these issues with conversion come up. System ffomega is one of the systems which fit into this much more general version of pure type systems. And also Guillaume Alley helped me out when I ran headfirst into an agdo compiler bug, which is not hard to do. So, okay. Thank you. Okay, and some quick final thoughts as closure of the academic track that, first of all, thank you everyone for joining the track today and for all the discussions. Maybe just a few thoughts from my point of view. I was a PhD student back in the end of the 90s when I became familiar with Lawrence Lessig's code and other laws of cyberspace and the now famous Code is Law Tenet that he popularized at the time. And what a better example 20 years fast forward to deal with smart contracts and what a great manifestation of this powerful idea. So looking back, it was programming languages and programming practices at the time and even today after the challenge for this. And the answer is clearly no as we've seen on and on with all the failures we've seen in systems that are being developed. But this is changing and today this event is also a testament on that. So it's very, very inspiring to be here and thank you all for joining the academic track of Lotus Fest. There is now lunch and Lotus Fest continues on the main track. So thank you everyone.