Video details

TypeScript for F# zealots - Tomas Petricek

TypeScript
05.29.2020
English

Tomas Petricek

If you ever talked to a functional programmer, you know that they'll use functional ideas in any language they use. As a long-term functional programmer, this is exactly what I did when working on a recent project using TypeScript. In this talk, I will reflect on the good and bad of using functional programming ideas in TypeScript.
Contrary to the title, this talk is suitable for anyone interested in TypeScript or functional programming. I will discuss a number of powerful functional ideas and show how they look and work in TypeScript. You'll see which of those fit well with the language and which of those are better left for more functional programming languages like F#. In other words, this talk is about what makes functional programmers coming from F# happy about TypeScript and what makes them sad.
Check out our links below, and don't miss our next conference! https://www.ndcconferences.com/ https://ndccopenhagen.com/

Transcript

Thanks for joining the typescript for F sharp zealots talk, say this is. This is talk I'm doing. That's not primarily about Assad, which might be a surprise to some people watching this. As as many people who have seen some talks from me know an F sharp person. But I thought I'll I'll speak about some different experience that I have had recently. So I couldn't find better illustration for zealots than this meditating. Meditating kitten who definitely knows what's the best programming language to use. I thought everybody needs a meditating kitten in the end these days. So this is going to be a fairly opinionated talk and I'm hoping that people will disagree. But some of the points. And we can we can sort of follow up in the in the comments. I'll be on slack as well. If people have have more things to say. So why should you why should you listen to this talk? I'm going to be talking about my experience with using typescript for a for one project that I've been working on. And I'll say a little bit about the project first, just so that, you know, what kind of what kind of things. I've been I've been doing what sort of motivated the talk. And then I'll I'll say a few things about TypeScript and I'll I'll cover both the sort of the things that I felt were nice about it coming from an FSR perspective and the things that I thought, but not so nice coming from an expert perspective. Now, I don't really expect everybody to be an F Saab sell, so the title of the talk was a bit misleading. I'll show you some some relevant bits of code. But it's going to be fairly introductory. So that's the thing I've been working, working on. And this is this is a sort of project that we've been doing using our base, have been using TypeScript, as this system that I've been building in collaboration with some people in the L.A. Institute in London, which is trying to produce a new notebook system for doing data science. And it's does a few interesting things, which I'll I'll talk about. And the important bet is that there's a client side that's fairly non-trivial or does some interesting things and that has been implemented in TypeScript. So what does this actually do? There's a few things that make this this project interesting. First of all, if you're doing data science, then these days, typically your entire sort of workflow has to be in one language. Typically, Python or R, we're doing something where it's easy to mix multiple languages in a single project. We're also doing quite a lot of clever work to track dependencies between different different bits of code in your system, in your code, in your notebook. So that when you change something at the beginning, the things that are later on get invalidated. We want to be able to do more in the browser. So if you just sort of, James, some interactive data exploration, that is something you should be able to do straight in your Web browser. And we have this feature called A.I. Assistance, which are clever tools that help you with with some of the common boring data wrangling problems. So you can actually try this yourself. If you go to rattler dot org. We've got a button which which will load an instance of the system on someone else's computer in the cloud. So you can sort of experiment with it quite easily. And what you get is this is this. I'm going to make this a bit bigger. Just it up. It's easier to see. So you get the way the system works is that it integrates with the standard environment that data scientists use called Jupiter. So this is not really a sort of standalone system altogether. It integrates with what people are already using and it adds a new kind of notebook. So this this this whole thing that I'm scrolling through right now, this is actually the bit the running the bit of code that we wrote. So it's hosting. Sort of standalone notebook inside this desk, bigger. And I can do various things. Here's a sample in Python where I'm just creating some sample plot. So I'm not really a data scientist, so I'm not going to tell you much about data science. I'm just going to tell you about building tools for data science. Now, the more interesting thing is here. So what I'm doing here is I've got a cell or code block written in Python. And what this does is that it uses this Pandit's library to read a CSP file from Get Hot. And it read some finance charts and then I can evaluate that. But following that, I've got JavaScript code block that loads the plot early data visualization library and I've got each other's scripts code block that access this. AAPL data set that I loaded in the previous Python code lock, so the really interesting thing here is I can mix Python and JavaScript, and if I run this without any sort of explicit passing of data between Python and JavaScript, I'm able to build nice interactive charts. So this is showing some finance data. My zooming skills are quite bad. So that's one thing we are doing. The other interesting thing is how does the tracking between dependencies work? So in a typical data science system, what do you get as you just have to run through the code sort of from top to bottom? And if you missed something at the beginning, later on, you'll get errors saying, ah, you didn't run. Are you a variable? One is undefined. So this system does something more clever where here I'm defining data sets one, two and three in JavaScript, Python and in R and then I have a bit of code again, JavaScript, Python and ah, that just connects all these three data frames together. And if I tried to run one of those, then it figures out that it has to evaluate all these, all these cells that I had at the beginning. So it does that does create some data frames and then it evaluates the one I clicked on. But it doesn't evaluate. Another sort of cell that I had earlier in the notebook that doesn't really wear the current one doesn't depend on it. So it does it does a bit of clever sort of tracking to figure out what depends on what. And I can even visualize this. So we have this little visualizer where you can see how the different bits of code all depend on each other. So there are some cells. Those are the bluish markdown cells, which are just code just commands. They don't depend on anything. But then. There's there's some are code, some Python code and some JavaScript code. And you can see that this depends on the inputs and outputs depend on that. So this is what we feel, what we've been building. And I just wanted to give you a clear sort of quick, quick demo of the system itself so that, you know, what's what's my what's my sort of background where I'm coming from. So the client side, what I was sort of playing with in the browser, that's all implemented in TypeScript. And as I admitted already, I'm an expert person. So why did we end up with TypeScript? So this was this was a project with a larger team where more and more people in the team knew, TypeScript. And that was one of the motivations. And I wasn't really working on this full time. So it was it was quite important. So so that sort of work in a way where others can be in control and and do things even when I disappear for a few weeks. So that was one of the reasons for choosing TypeScript. The other thing we thought was that the Jupiter platform itself is implemented in typescript. So if we do the same thing, then maybe people might contribute a bit more, which is a nice, nice idea in theory. In practice. That actually doesn't happen. So getting outside contributors is always difficult. So what are what are the sort of reflections on TypeScript based on that project? And this is again, this is sort of reflections coming from an SRF perspective. So maybe it's sort of interesting and not necessarily a mainstream view on TypeScript. So that's that's why I thought this was an interesting thing to say. The good thing is that you can actually do fairly nice functional programming in typescript, or you can use the sort of elegant, beautiful, functional architecture. And I'll I'll spent quite a bit of time showing how that worked. The bad is that the typescript system does make some difficult tradeoffs. The type system. And this is this is inevitable because that's how TypeScript was designed. It's sort of designed to deal with food, JavaScript world. So you have to make those changes or make those decisions. There's nothing else that could be done. But it does make. Certain, certain functional programming patterns. Quite annoying. And one that I found, which I called the ugly here, is you would think that typescript has this sort of simple, simple JavaScript syntax. But that isn't really the case anymore. So I'll have a few complaints about syntax. It's not quite the what talk. But you'll you'll see. Well, what happens. So I want to start with the good things so that those of you who are interested and I'd like TypeScript can watch the first part of the talk and then switch to some other session. If you don't want to hear me rambling about the bad and the ugly. The good thing. So the way we structured our system is we're using this Elm's style architecture and we've actually implemented quite a lot of that ourselves, except for one one key. And the reason and the nice thing in this architecture is if you're using functional style, you really just have to immutable types representing the state, the model and aband. What is happening in your in your notebook? And then you have two functions, update and render. And they're purely functional. And our implementation has very few dependencies. The nice thing about this model is that it's really easy to debug and understand what's going on. And in a system that's fairly interactive, where sort of cells are evaluated in the background. And stuff like that. This has been quite valuable. So why why not just use react? And this is a question that I think is is important because I'm I'm going to show you how to implement a few things from scratch. And I think this is very often sort of good approach. If if certain things are true. So our user interface in the system is not very fancy, like it is really just the speech. So the fancy things here are well, there's a few buttons for, like moving things around. We're using the monocle editor. That's the only that's the only sort of rich component that we need. But we're not building, like, complicated forms for entering dates and stuff. Where we act has a lot of compliments. So we don't really need a lot of components. The other thing is that this is a project that's been going on for a few years. And it's sort of done at an academic pace where you get back to it sort of every every once in a while when you add new things. So it's not exactly great if all your dependencies completely change between every time you sort of get back to your system. I think this is something that's fine if you're sort of constantly working on a project every day or every week. But if you have longer gaps, then I think having fewer dependencies is actually what makes the development more sustainable. And we do like the fact that we have more control over how things work, because, for example, here you need to integrate with the Jupiter environment. All the stuff around. So you do need a certain level of control of how the user interface is rendered. And it turns out that the core logic that that react or all these sort of modern programming methods for the Web, what they what they do is actually pretty simple. So. I'm going to show you that. I'm going to show you the way these architecture works. And for this part, I want to show you the of code first and then how the same things look and same thing looks in typescript. So this has really two purposes. One is, I think the F sharp version of it is quite clean and it conveys the idea very well. So I'll show you how to do this and f sharp to explain how things work. And if you're familiar with. Or you can you can use the F sharp part to make sense of it. And then sort of see how the same thing works in works in typescript. And I think that the other thing that that sort of typescript version will then help you understand what's going on in F sharp here. So I'm just going to start my simple project. And I've got all my code here already. So let's just let's just wait until the backpack server launches. So here in F Sharp. I'm using fable, which is f safty JavaScript compiler. That takes my F sarp code, produces produces JavaScript and passes it through the all standard JavaScript, things like backpack for, for building and life reloads. And this is running. Seven. Oh, seven one. So right now, I've got a page that says, Welcome to counter. This is not going to be a super, super inspiring demo. I'll just create a little counter where I'm going to count number of clicks on a button. So the way to do this is I have two types that represent model and aband and model is my state in the application. So in my state, I'll I'll just have count. And initially, let's start with zero. And then when I'm rendering it, then I'm going to I'm going to put. I can say current count. And then at the at the current count and the current count. It's something that comes from. I need some more prentice's from this state. State variable. So the way the system works is that I define what's my state. I define an initial state. So my state has count. It's an end initially. It's zero. And then I define a rendering function that uses this little domain specific language for generating HDMI oil. And it can access the state if I save this. Then I get current count to zero. And I can add some buttons to increment and decrement. So I'm going to create a button and this is going to be text plus one. And I'll add a button for minus one as well. If I save this, I now get my buttons. And how do you handle events in this architecture? So the idea is that you define a type for Evans. And here, I'll just define a while, I must say, which says update the state. And it's going to take an integer as an argument, and that's going to be plus or minus one. And in the user interface, you can then trigger those events. So I can say, well, ever click Hepburn's? On the plus one button, I wants to trigger the update event with plus one and whenever Klegg happens on the minus one button. I'm going to get minus one. And the key thing is I now have to implement this other function called state called update, which takes the current state and the message. And we're going to pattern match on the message. If this is update by some number, then we're going to return a new state. Where the count is the original count plus the difference. So I think this should this should actually do. All I want it. Let's see if it if it can count, can it count? Plus 16 minus works. All right. So this is the core, the core thing. Now, I want to make one more change. I want to do it so that the count initially is not started. Like you start with the counter decimal initialized. You have to click the start button. And the excerpt way of doing this is to use the option type. So now I'm saying a count can be a number or it can be not set. And initially, this is going to be none indicating this has not been set yet. And in my rendering. And I'm going to save a bit of typing here in my rendering. I'm going to pattern match on the count. And if this has not been set, meaning I haven't started counting yet. I'll generate a pay page with welcome to Counter and a start button. If the current count has already been stopped, the counting has started. I'll generate a page with. And the start button is going to send a new type of message, which I'm calling reset. And one nice thing in F Saab is that all those things are checked. So I get the message here saying in my update function, I'm not handling the reset message. So if I get the recent message, then this is the case where I want to return zero to initialize my counter. And I'm also going to have to pattern match on the current count. So reset that ignores the current count. But if I'm ignoring, if I if I'm updating, then I need to get the count and updated by a certain number. And this is this is going to be set, which is indicated by some case. If I get update, but I haven't really started counting yet. This is an invalid operation. I'm just going to ignore it, although you should probably throw an error message in this case. So if I if I save it, I have a counter where I can start and then I can count. So this is this is all I wanted to show and start to shut this down. And I think this is really, really a nice approach to implementing reactive applications where you're in sort of it's not. I wouldn't necessarily use this if I was using something with, like lots of forms where there are standards, components for doing that. It works really nicely for this case because we're basically creating our own user interface. And FSR has this really nice way of modelling. Modelling domain logic. So this is another example where I'm saying I'm implementing a to do list. The model is the list of to do items to do item has the title and the due date. And what can happen is I can add a new item or I can reset. And in the oil market, actually, you have these two operations update and render where update takes the current model and a message. What's what's happened? And it produces the new state new model render takes mainly the model and produces an HDMI output. And it also has this trigger operation, which is this first function here, and that's used for triggering events. So can we do the same thing in TypeScript? There's a few things that I'm going to be using. So first of all, I really like the way I've of type checks everything. And if you're using typescript, then typescript has various flags that you can turn on for enabling various security checks or checks in the type system. So there's strict Moul checks flag to avoid nulls and no implicit any to avoid this any type which means any any JavaScript value. So this is this is really useful. And we've been using that. And there is a flag just strict, which turns all these things on. So I'll show you how this how this works. And it turns out that you can use TypeScript actually reasonably well for implementing both the record types that I was using and F Sarp and D discriminated unions that I was using for representing the messages. Now, those aren't really immutable. So here you just rely on being disciplined about how to use them. And it's one thing that we didn't always do well in our project. So we did. OK. Let me state something by accident. And then ended up scratching our heads for for half an hour to find where we get that. So what I want to do now is do basically exactly the same thing. But in TypeScript. So I've got the same stub implemented here and I'll start at. And in this project, and I'll send link to the code. The only thing that I'm not really showing you as this is this Alimi is not P.S. file, which is using the market library and what the library does for us is the updating of the Web page updating of the dog. So this is the sort of standard virtual Bob trick where we're not really replacing the whole the whole content of the screen. We're just updating bits there. But you could see that this was this was all my code. Here is some is that 50 lines of code. So I'm not really hiding anything complicated and I'm just using one external library. And I'll do exactly the same thing as I did before a F sharp, so I'll have a type representing my state called model and I'll have a type representing my evidence and then I'll have a render function which says, welcome to Counter. And an update function where I do the update when some some event happens and I have an initial state and then I just call my helper to start the app. So this I've got running on a ladder of port. There is just to show that this is actually. Running, if I say from typescript, this should sooner or later reload. There it is. Three. Says Well come from. So I want to recreate the same thing I did, I left sharp. So the model this is this is going to be quite easy. I'll just have count. And that's going to be a number. And the more interesting thing is how do you actually represent those evidence? So I had two events. I had reset event and the representation you can use and type scrape relies on on two things. It relies on union types where you can say a type is one type or another type. But it also relies on this on this very interesting trick with Singleton types or where I can say a reset event has a kind and the type I'm using here is not string is just a constant string called reset. And a recent AVM just doesn't have anything else. I also have update event. And this is going to have a kind update and a number by how much? And then the event is either reset event or update event. So this is really pretty much the same as the FSR discriminated union. And, well, we'll see that this is this is quite interesting. When we use this the way I'm going to do the rendering. Well, first of all, I should I should do my initialization correctly. So let's start with count equals one on one. You can see the biggest problem for an FSR person coming to typescript is you have to use colloids. And Bolt was. And this bit here is the same kind of HDR Mellgren drink. So we're going to render. An H1 with the current count. Current count is. Followed by we get the count and convert that string. So that should should be a good start. And now I need a button. I'm going to spare you some of my some of my typing, slow typing attempts and copy those two bits from here. So I've got two buttons. And one is the increment button. The other is the decrement button. And they do have they do have unclick avam handler. Where if I click on the bottom, it will trigger the attempt. Now, the interesting thing here is when I'm when I'm triggering the event. This is really determining that I'm triggering the update event. So this is producing a value of the update event type. And the interesting thing I need to do here is do the updating. So if I get if I get well, let me first see if it actually runs. So if I save this, I do get. I do get current count, this one. So that works and my buttons don't do anything yet. So how do I get my buttons to work? Let's see. And this is the bet. This is the. This is the really interesting. So if you do V.T. Dot, then you can see that the event, which is my my other end, has this. Has this property called kind and kind is of type which says this can be two strings. It can be either the reset string or it can be the update string. Now, in some cases, if this is if this is update, there should also be a property called by because then we are specifying whether it's plus one or minus one. How do we access that? Well, this is the bit where TypeScript is doing something quite interesting. Cos if I say switch over to Evan kind. I don't think that if the case is an all and actually the editor even knows this. The editor knows that EBITDA times can only be two things. It can only be reset or update. So if it's reset, then we're going to return a new state where the count is zero. If this is update, then. And this is where things get interesting, because if I do e.V. t dot, I do actually get by in my out to complete. So how does this work? TypeScript knows that event can be either a reset event or update event. And it knows that this is determined by the value of the tined. Now, if the calendar is reset, there's nothing else. But if the kind is update, there's this buy property here, which I can now access to increment my current. So count is going to be the original count. Plus by. So with a bit of luck, this should now make my incrementing and discriminating work seems all right. And one last thing that I haven't implemented yet. Is this the start button? So I do have a improved version of my rendering function here. Which does the same thing that I was doing in an F sharp before. So if stay to that count, it's not. Now, then I'm counting. And I'll render the current count. And do the plus and minus one button. Otherwise, I'll just say welcome to the counter. And we will start with count being Mel. Now, if I if I save this. Then I have the start button and now I come to counting. Now one thing I haven't done here really is I haven't dealt with these Knoller's very nicely. So I just said count to now, but count is actually a number. And if I did know, if I wasn't careful, I could I could get various errors caused by now. So this is the default sort of bad behavior you get. You get in old school JavaScript. But the nice thing in TypeScript is that I can read enable these strict null checks. So if I in my config say strict Melcher's on. Then suddenly I get a bunch of errors here. What do I get a bunch of hours? I get an error here. So this is not the most useful place for getting my error message. The main issue is that this actually does a sort of. Well, TypeScript doesn't infer that this is model because it's not model. It couldn't be model because the model doesn't allow no. So I have to change my types a little bit. And I quite like this. This the way to the typescript handles now is because you can say account is a is a is of type. No or no. So you're explicitly saying you can get Mal here. And well, if you do that, then suddenly in my update, icons just access access count and do calculations with it because it can be No. So I have to do the same thing, which I did in FSR, in Art. I use pattern matching on option, but to use here, I have to say if stapled count is not now, then I can do this. Otherwise, I can just return state, and that's all I have to do to fix it. And this is sort of pointing out a potential issue in my coat. And you have to be explicit about about your your nose. So I do like the strike option. And it's pretty much the same as what I did before with F Saab and option types. So option types are sort of a way of doing the same thing without having all this extra machinery in the language. Now I can be even more strict and turn on strict, strict type checking. And what this does, it gives me an error here. So one thing that's enabled is that I now have to explicitly, while I'm not allowed to have to have variables which will implicitly have a type any. So in typescript, that any type is a very useful thing for Interop writing with JavaScript, because if you don't know well what object you may get from JavaScript, you just say it's any. And you can do anything you want with it in typescript. You can turn this off. And now I have to be explicit about my types. The sad thing is that Difret doesn't really infer all the types. So here I have to do it manually and I have to say that trigger. Is a function which takes an event. I guess I have to say it takes an event and produces Unit Boyd, but you have to say you have to call it somehow IBT. So I think all those type annotations is something I don't find very pleasant. So that was the counter demo and the main point here is really I think this is that this is a very nice architecture and it works quite well with TypeScript. And here I'm just sort of implementing things from scratch because that what suits suits our project for. For two reasons. The user interface, we're building most of it ourselves. So it's a very custom UI. That's one thing. Second, this is something that's going to be sort of updated every single day. So we don't want to rely on the sort of breaking changes by others. So in typescript, you can model what F Saab knows has discriminated. Unions using using these interfaces with Singleton types. And the union constructor. And you can put this nicely with the L architectural operations and the type signatures of those I think are somewhat painful to read. But it's the same thing as an F Saab. So you have. Update takes the state an event and produces a new state trigger, a render takes this trigger operation for notifying the system that something has happened. State and produce system, HDMI load. So what are the interesting reflections? I'll demonstrate this using a few few examples. So the first example and coming from from out of Rattler project is that brettler is extensible and you can add new kinds of cells. So we had Python JavaScript marked down, but you can you can build your own cell with your own user interface as well. And to do that, you have to implement some interface. So this defines a what is a language plug in a language, plug in defines the language. That's just a string, has some ed, which is a mother object. And then it defines how you pass code. How you evaluate code. How you stiff code. And how you bind, which is our our name for a sort of step that happens before evaluation where we construct dependencies. So all those things you have to implement. And in our actual actual system, I've got I've got one example. Where we're implementing a little language plug in called merger, where and this is just the devil. It's not it's not useful for anything practical where the idea is you have a little little language that all can all you can do is it can merge data frames. So if I turn this on and save my files. I've got another instance of rattler running here as a stand alone thing. So this now reloads. And I turned it on and I now have a new cell type so I can add the merger cell. And this has this is this is using a little while programming language, not even programming language where you just say output data frame name. And comma separated list of input data frames. So this is a domain specific language for merging, merging data frames. It's just a demo. And if I evaluate this, what happens is it now merged two of my data frames. If I add the third one, then it it merged three of my data frames. So there's there's a bunch of coats running here in and the relevant bits as I need to implement this interface and in my implementation I decided to just create a value using the object notation. So this is something I find quite nice. You can you can sort of define interface and implement that by constructing an object that has all the required things. If I remove one of the things that is required, then I get an error message saying you're not you don't have that. So I'm implementing that object here. And as part of the work. And this is. This is where the sort of interesting logic comes in, the way rattler works is that all those all those data frames that I've been merging, they're stored in a compliment called DataStore and. Python or R will put the data into DataStore. And when some other component wants to access that, they will have to fetch it from the datastore. And that the result, we could back to the datastore. So here we have some code that calculates the current value and puts that into the into the data store. And this is a place where you just inevitably have any values because you're working with things that you get from the back. Now, the other interesting thing is that this actually. Well, I'm not I'm not I'm not showing the getting off the data because that's in a separate file. But I'll I'll. Get back to that. So. I think there's a few interesting things that I find quite tricky in TypeScript. One is this sort of you can you can often do object oriented programming with classes and interfaces. And if you're doing more object oriented style, that's sort of inheritance, then you do want you know, you do want to use classes. Another thing that you get with classes is you can actually check veteran value as an instance of a class using this instance of the instance of operator. And this does not work with just an interface. It only works with classes. And if you want to have a runtime checks, then you do need that. If you but the sort of object implementations of interfaces, which is what I've been using, give you somewhat lighter syntax. They're somewhat easier to use, but they don't give you this sort of ability to do the checks. So one thing that we've struggled with occasionally in our case was we just started with, um, with an anonymous interface implementation, using the object, like what I'm what I'm showing here. And then for one reason or another, we decided we need to turn this into a class. And it turns out that you can you can do that. But the annoying thing I'm I'm switching between the two versions is that pretty much every single bit of syntax changes. So here you do Kolon. Now this becomes implements. Here I say equal curly bracket. Now I just take currently bracket in the code. Previously I had columns everywhere. Now I just have equals everywhere. My function previously was defined as a as a attribute which is implemented using the arrow notation followed by curly brackets. Now it's. Without that, just as a function and the body, the body stays the same. So this is one case where I thought there's there's some pretty crazy inconsistencies between the two different ways of doing the same thing. Now, it makes sense because that's how that's how JavaScript works. But I think it's this is one of the examples where the JavaScript heritage of typescript really means that doing certain things is quite painful. The way you do constructors and classes in typescript is the sort of standard way you define a constructor. And in that you initialize all the fields and to someone coming from an FSR background where FSR has these beautiful implicit constructors, where you just as you're defining the type. You take all your arguments and then you define all your fields. Switching to the typescript world where you need to have explicit constructors is just that. That's been made. It's just it's a tiny thing. But if you're writing a lot of classes, you'll end up wasting a lot of time. Now, there is a few places where where I managed to break the type system or break where the type system sort of does things according to the necessities of the JavaScript environment, which aren't very pleasant. So one of those is when you're when you're using fetching some data from HDTV. Always get response or the data, which is of type any. And then if you're not careful, you can very, very easily get into trouble by sort of propagating that any value somewhere else in your code and causing all sorts of troubles. And you can remove some of those places where this error can happen by using the strict mode. And if you're if you're doing the same thing in F sharp, there's. I think FSR type providers are one of the nice, nice features that can help you with that when you can sort of strongly type your responses. Bob? If you're using fabled f f sharp, you'll just. This is a place where you will easily notice that this is a problematic place. So you have to define some type explicitly or you just won't be access. We'll be able to access it that easily. So that's where sort of f sharp makes things harder for you. But I think it's actually a price worth paying because it just removes quite a few errors. And I do have one one very curious example. So this is a place where the fact that typescript is still just the JavaScript runtime underneath does cause a few few surprising issues. So this is a this is a. Bit of code that is actually pretty much based on what we had. We have this interface. Representing a variable, a variable has a name. And we have an object with which we're using as a as a lookout table. You can sort of use the square brackets to index into it. And it's basically just a map or lookup table where given a variable name, you'll get the variable. And I have a little function which returns the length of the name of a variable to takes the scope, which is our variables or data frames in scope. The name of the variable. And it says if this variable is not undefined, then return the length of the name. Otherwise return minus one. And this is this is even in the strict mode. So this can't be No. And the question is, can this ever fail because because the name is undefined. So in my type name is a string. It can't be undefined. Otherwise you'd have to say name on string or undefined. But it turns out because this is underneath JavaScript. If you just call this and use empty lookup table and to string as the name of the variable, then these breaks and your code crashes because this lookup is just the object lookup of JavaScript and all objects in JavaScript have to string. That's the method that you can use for converting those to string. So this is not undefined, but it's not actually as our type requires a variable. So it's a it's a function. It's not a variable. And this is, I think, a case where the fact that we're building on JavaScript typescript can remove some of those quirks, but not all. And this was this was a case where we released this sounds pretty arbitrary, but we really did spend an hour debugging this issue because we did have a variable called to string. The last bit I wanted to talk about is some of the things that I didn't find particularly elegant in TypeScript, but that we're not really a major issue. So this is mostly about the syntax. And TypeScript inherits the JavaScript syntax, which is really sort of designed for not necessarily functional style of programming. So this is perfectly expected. That's a price you have to pay when you want to use TypeScript in a functional way. And I'll I'll. This is, again, based on an experience of writing a piece of the rattler system. Well, one of the one of the really sort of nice, interesting, innovative pieces of data science to link we've got are these guy assistance? Which are our tools for semi automated data wrangling? And I'll show you what this is. So I'm going to open another demo and you can go to rattler dot org and play with the Slater on your own. So I've got one example here where I'm downloading some data on air traffic accidents, which is actually originally coming from Eurostat. And the way the data is structured. Is that you have a row? Representing accidents related to all airplanes that were registered in a particular country where the accident happened in a particular country. So, for example, here, this would be a road that represents accidents that happened to aircraft registered in Austria. That happened in Cyprus. And it's very sparse. One of the issue with the data, though, is that. They also include aggregates. So there's there's some rows that represent the total for the whole EU. And using that is quite tricky. And what I can do is I can say I want to use an assistant for outlier detection on my data set. And this will create a cell with custom user interface where I can just run the assistant and then ask it, how do you recommend I fix this code? And the assistant says you should add a filter where C Reges is not you. Twenty eight and also G.O. is not you. Twenty eight. So it looks at my data and finds the outliers and. And it recommends that I add certain filters. And I can see what rows it actually removed. So this is a tool where if you're cleaning data it can help you with some of the typical boring tasks. And in the implementation of this, there's a few things that I found quite annoying. One is that sometimes you have two ways of defining a function. You can. You can do that using the sort of lumbar syntax which works in certain contexts. Like when you're passing the function as an argument. So. Here, I'm defining a function that takes up a list of A.I. assistance and generates an editor where the editor has certain initialization code. Now this is written using lambda function. But if I need to change it to an explicit function, then again, I go through this process of making so many tiny tweaks in the syntax. So here in the function notation, you just say curly bracket return. And then you return something in the other notation. You have to say Arrow. And then you don't need return. But if this starts with curly brackets, then you need extra apprentice's around because otherwise to interpret the passerelle treated as a code ball. Another thing that I find I find quite tedious is in the code that constructs these HDL blocks using the H notation in typescript. I end up with a lot of help definitions and then I end up with sort of one messy condition that says depending on the state to do this or this or this and in F sharp. This was actually this is this is one thing that can be handled quite nicely with. List comprehensions where I choose, define a list and then I say yield this user interface element or this element or this element. So you can embed quite a lot of logic in this code that constructs less. So to fight to to wrap up. So I talked to talked about FSR, about TypeScript from the perspective of an excerpt person and and also from the perspective of a very specific project where our project sort of is long running, not very, not very sort of ongoing all the time. It's reimplemented, a plain user interface from scratch. And TypeScript worked quite nicely with this architecture, and I did like the fact that we sort of implemented that ourselves, had the nice control over that. TypeScript isn't really designed for functional style. So some of the things will end up being ugly and in some places it's trying to be JavaScript, in some places it's not. And this often leads to random surprises like this to string thing. I would I would I sort of when would I prefer FSR? When would I prefer TypeScript? So I'm obviously biased here as an FSR person. I always choose F sharp, but I was working in a team where this was the team decision and we did that go for TypeScript. How did how do the two compare? So if you're using F support above with Fable, then I think the remarkable thing is that F Saab has this functional core which is safe and clean and works. And the way Fable compiles this is that it avoids most of the all the all those sort of messiness of JavaScript. So you have the core language which works nicely, is predictable, is understandable. And then there's the extra integration code, which inevitably has to make tradeoffs, typescript supports many different styles, including sort of object oriented and functional. And it doesn't really hide JavaScript. It's sort of a JavaScript is one of the styles that site type typescript supports. And unlike in FSR, where the core and the integration is quite clearly separated, I felt like in typescript, this is not so much the case. So you always end up mixing different different debates and you can do it well or you can do it not while. And it's really hard to sort of figure out what is well, what is small. So to go back to my to my meditating, meditating kitten zealot, who knows that SRF is the right choice for everything all the time. I think the important things here from the talk where I was actually actually quite sort of pleased with using TypeScript in the way we use that, using the sort of plain Almaz alarmist architecture, you have to be very disciplined and you're not really. You'll you'll end up using typescript in a way that's not obvious. So you. I sort of thought if we go for a top four type scraped, people won't have to learn too much. That's not really the case. If you want to do functional programming and typescript, you have to learn functional programming. And TypeScript. Just like if you want to do FSR, you have to learn that Sarp so that that learning is still there. TypeScript in a functional way is really different language than TypeScript and a JavaScript way. And a lot of these decisions really depend on the project context. So you always have to have to use your use your judgment. That's all I wanted to say. And I'll have a look at the questions now. So thanks for tuning in and for sticking to the end. All right, question, is there a reason you're using interface as opposed to type system? Seems to me that the interface is just a reference to. I have to move my window here to Microsoft's inclination to OPIS. Yeah, I think that this better is something I find sort of. Confusing as well. Like, there's there's so many different ways of doing things that I just ended up using interface, because I think that's the first thing I was I was able to get to work. And I don't really have a good good answer to that. It's true that it's true that this this might be the case where TypeScript just sort of inherits a lot of. Different styles, and some of them are more, oh, like if you use oh, you might end up with classes and interfaces. If if you're using different style, you'll you'll end up more with with types. So there's no there's no good good reason for this. It's just a random decision based on what I got. What. Not what I was able to go to get to work easily for what I wanted. But, yeah, if you look at some of my type definitions sort of earlier on. There's there's cases where you need to say type, there's cases where it doesn't really matter like here. This has to be a type because I'm defining a type alias. By the way, you define the way you define the record types. I don't really know if they're sort of subtle differences. All right. Any more questions? I don't think there is there's any other question here. So I don't think anyone's anyone's joining. Life on Zoome. So if that's if that's it then. Thanks again for joining. And I'll post all the code, including, most importantly, the two F, SERP and typescript comparisons sort of side by side of the Elvis architecture, which I think is a is a fun thing that people can play with later. Thank you.