 Do you see? Yeah. Okay. Sorry about that. Can everyone read that? Yes? Yes. If Paul can, I think everybody. Yeah, we should. Another line. Yeah. Yeah. Where is that? I don't know. I don't know. Take it, sir. Take it, sir. Take it, sir. Take it, sir. Take it, sir. Take it, sir. Take it, sir. So, I'm going to talk about algebraic data types, and it's going to be an introduction for very early beginners. Yeah. So, this is the first talk in a series of talks that we're going to try called 101s. So, this is the ADT 101. It's also an experiment because I'm going to do it with the working Askel module and Dock test. So, this is a DIMM session, and I am able to run, to actually load the file in an Askel compiler and then run the Dock tests. You can see here the Hello World. This is how Dock tests work. So, this is what you could enter in an Askel REPL. And then it checks that the output that it gets from an Askel REPL actually looks like that. Here you can see that the exclamation mark is missing. Can you see that on the right? Yeah. Yeah. So, I forgot the exclamation mark. If I had that in, I would actually see that this works. Yeah. So, but this is a bit of an experiment to see whether it works to give a talk with a module like this. I hope that the talk is going to be more interactive for that. So, yeah. If you have any questions, I want to try out something I can edit the code and then we can see what the compiler says and what that works on. So, feel free to intercept. So, algebraic data types are very fundamental in Askel. They are much more fundamental than lots of other stuff that people talk about, like type classes or type test instances. They are much more fundamental than monodes, for example, functions, monosransformers, or other related concepts, a category, if you will. Yeah. I'm going to stop. So, they are definitely not more fundamental than polymorphism. You can't claim that in this trade. I mean, polymorphism comes into play even if you don't have ADPs at all. Yeah. In the minor class system, without ADPs, at least five different students. Well, as I have a BIM session, I can delete that. I prefer that. Because it's a beginner's talk, right? I mean, that's not a discussion to have in a one-on-one talk, I guess. Yeah, fine. But I think it's important to emphasize that ADPs are very fundamental because it's very easy to get confused by the typical learning materials in Askel that usually center on monodes. And monodes are not the thing that you should learn first. The first thing you should learn first is ADPs and maybe polymorphism and higher order functions and some of them. But yeah, I'm going to talk about ADPs. The target audience, as I said, are absolute beginners. I do have the ambition that some intermediate Haskellers will gain some intuition for data types. But I'm not sure that I succeed. Let's see. So the first concept that I want to talk about are so-called product types. Here you can see a very simple declaration of a product type that encodes a two-dimensional position. And what happens here is that you declare two things. One thing is a type that is called position, which is indicated by the word after data. And the second thing is a constructor, which is also called position. It's not a problem that they have the same. They don't have two, but it's not a problem that they have. And it's common that they do. And a product type is a Cartesian product of its fields. So this product type has two fields, both int, one is the x component and the other one is the y component. And with that, you can construct values of the type position using the constructor position. And you just stick two values in there that are both of type int. And then you get a value of type position. You can see that here. So this then you can say, well, I want to have a position where the first field is three and the second field is five. And then you get that. You can also do a bit more complicated stuff. You can say that the x component is some calculation. And then this will actually value it to this. It doesn't make sense so far. I think Victoria is not her inner ear. She is younger. Maybe we'll have to take a minute. Does it make sense? Yes. Alisa? So I think the term constructor is also used in OOP, for example. And Haskell works quite differently. And Haskell constructor is really something that doesn't do anything. You just stick values in it and then it contains these values. Sometimes I also think of a constructor as a name tuple. It's just a tuple of two numbers. And it has this type position. But it cannot do anything else. Is it just structured and like C? Yes, it's like a structure. Yes, exactly. Yeah, it's atomic. You cannot split it up anymore, right? Constructor is just like, cannot be looked at, kind of inspected further. And one interesting thing is that if you have a value of type position, it has to be constructed with the position constructor. There's no other way to construct a value of type position. At some points, it's going to be a position constructor. Of course, we can have a function like this one, make position. It takes an int and then just creates a position for you, which in this case, well, for example, if you give it a four, it will just set both components to four. But somewhere at the lowest layer, it is going to use the position constructor. One thing that I haven't... It's something that people don't talk about that often, I guess, but that I found very useful is something called implicit signatures, which is... So if you define a data type like position, you do get implicit things. And of course, talking about the constructor. And these implicit things do have types. And it's... For example, in our example, this is how you ask the Haskell compiler for a type of something. So here I'm asking, what is the type of the constructor position? And then it tells me position is a function that takes two ints and produces a position. Right, like if we look at the position data type, it's not completely apparent that that's what we get, but it makes a lot of sense when you think about the constructor as something that you stick the values in and then you get the value, right? So I think it's often useful, especially when we start thinking about these things, to bring these implicit types to your mind. That means that you will present partially applied constructives. You will show partially applied constructives. No, I won't show anything partially applied. Another thing that you can do, which is slightly... Gives you a bit more power for product types are selectors. So you can define... This is a very similar type, right? But you can, at the type declaration, also give the fields names. So this says the first field is called pause x and the second field is called pause y. And that gives you actually a function, pause y, two functions, but one is pause y, that actually does what we expect you to do and extracts the one field. It's like key for a passion. But when you extract the value by key, a map or a passion or something, so it's extractable. Yeah, a bit... It's very rude, for example. But it's a field of a structure, because it's typed. Yeah, I think it's more similar to... You showed a type of possible? Right, another. Yes, again, pause one and pause x do get implicit signatures. So what you do get is a function, pause x, that has the type position two to eight, right? And then there's no special syntax for accessing that field. You just get this function. But it's more similar to like a dot accessing of structs or something. Still think it's in so far a good comparison as that commonly it has the programs. You see rarely the use of HashMap or DreamMap, and in other languages we see them a lot. And basically we replace them by these records. It's useful, especially as it has to be. Most of the time you think you want to have HashMap, you don't want HashMap, you want this one. Because it is typed. Yes, I mean, the only reason you will ever take HashMap is if you don't know your domain, then it's a keyword, so your domain is open. Yeah, okay, and what is a keyword? Yeah, yeah. There's no... There's no, like, any keyword type of thing. Yeah, you can put new keys in there. I mean, it's just like the question of stuff, like, is it some, like, analog type or rough example? Yeah, yeah. Yeah, this standard, like, if you're coming from like a comparison point of view. No, but in a comparison setting, right, if you have a non-unit type language, then it makes perfect sense to have fields, typed fields, right, which are static, which is very different from a runtime domain for some associative data structure. It's very like a language-specific, right, because for some languages, you may have type variables, for some, you may not have, like, for example, for Ruby, you may have, like, type variables, just like this thing with, like, value variables. Right, no, no, no, no. I mean, it's like, unless your language is unit type, like Ruby. Ruby doesn't really make much of a difference because we don't have types, static types, anyway. So this whole distinction, but in, like, C++, which is, or C, even, right, which is imperative, but it still makes sense to have a struct with a static set of fields with static types. But even in C, that doesn't mean that you don't need associative data structures like a NashMap, right, because it's completely different thing for some kind of problem where, you know, you process something, find out what the keys are, then you're going to need a NashMap, even though, or not a NashMap, but some associated data structure, even though in C you have records with static fields. So this is more like the C record fields. Because it doesn't mean, talking about the C struct field names, you're implicitly littering your namespace with all the fields. That's like a word of Haskell. It's really bad. So yeah, what you actually do get is this function for y and for x, these two functions into your scope. So you couldn't have another type with the same few names for you now? Yeah, I would have loved to name these x and y, but I use x and y somewhere else. On the other hand... On the one hand you fire, what's this? Please ignore that for now. That's all. That has little to do with algebraic fatal times. Oh, yeah. But yeah, does that make sense? Any other questions? So, so far what we have done is only construct values, right? What we want to do with positions, what we also want to be able to do is deconstruct values because we want to do something with that. And here's a small example that figures out whether a position is valid. I'm talking about chess, by the way, if anyone is wondering what the domain of this is. Why are positions with integers and not doubles? Yeah. So what you can see here is that the position of type position to bool and the deconstruction happens here. It's called pattern matching. So what happens here is that we say, well, our input argument is of type position and then we can use the constructor to actually deconstruct the position value and get the x and the y. So what will happen is that the actual two fields will be bound to x and y. So these are arguments, parameters to the function now. Does that make sense? Yeah. Yeah. And it's valid. It behaves as we expected it to behave. Those are product types. And now the other fundamental concept with ADTs are sum types. And I have a very simple sum type here which is for color. Again, this is about chess. So what we have here are two distinct constructors that are alternatives. So the type of this is color. We declare a new type that's called color and it has two constructors, white and black. And if I write down the white constructor, I get a white constructor of type color. And if I write down black, I get the black value of type color. Again, we have some implicit signatures that are much simpler now. So white, yeah, I just said that. White is a type of color and so on. Again, interesting is that in some types, if you have a value of a certain type, you know that one of the constructors is being used to construct that value. So if you have a value of type color, you know it's either white or black no matter how it was constructed. And deconstruction of sum types works like this. So for example, here's a very simple function that just figures out whether color is black or not. And here there's a case statement where you say if my given color and then you get any pattern mesh on the two constructors. If it's black, then return truth. And if it's white, then you evaluate default. Yeah. No test case for black. Does that make sense? No. And these are the two concepts that you use in Haskell to construct all your data types that you have. So every data type is a list of constructors and every constructor has a list of fields. Right? So for example, here I have a data type for a chess piece and it can either be a pawn and a pawn has two fields of position and a color. Or it can be a rook and that again also has a position and a color. So every data type is a sum of products, if you want. But then you can declare as many data types as you want and then you can stick them together so that that's actually how you can build quite complex structures. So for example, I have declared position myself and position is a two-dimensional position of things and I have declared color myself and that can be black and white and then I can use those to declare a piece which then is quite a rich data structure and quite nicely models what a chess piece is. I included Queen here just to show that these don't have to have the same number of fields at all. Like that's not a restriction. If you wanted to then you can do that but you don't have to. If there are any other chess players here then you can use the logic to choose. And again we have implicit signatures and I think it's useful to look at them. So for example the pawn constructor has the type position to color piece. You get a position and you get a color and you get a piece. The information that it's pawn and not a knight is encoded in the constructor. Rook has the same type and Queen has a different type and it gets this Queen originally. It has this additional field. Yes. We can of course then construct use these constructors to construct something here. I construct Rook and what what. That's actually the correct position. So given that you've got this literary problem you now use the words original and created which are kind of useful words. Which I can't use for what? Yes. So you've implicitly created posex about what happens if you say is original or posex. What kind of error do you get? Well if... Let's try to reduce it. So... For example if... Creative was a posex. Well I mean this is not useful at all but if I did this then I would get this error message. Multiple declarations of position. Of course it sees that as reading. Well that doesn't work in Haskell, right? Like a Haskell declaration is a declaration and it's valid for all eternity and that's why this won't compile because it says like well you have two declarations for positions so which one should I take? Fucking weird. So yeah I mean this is a practical problem. You can put it in different modules and then you know figure it out through qualified names but yeah this is actually a problem to practice sometimes. Yeah this is an example of the deconstruction of this more complicated type called peace where we first have a case statement on the peace so the idea is that this function returns all the loud positions assuming that there's nothing standing in the way. So what we do first here in the first case statement is that we pattern match on the peace and we say like well if this is a pawn and then you can then recursively pattern match on the fields also right? The first field is a position and you can pattern match on that as well and directly get the x and the y in this case we also need the color because white pawns can move differently than black pawns and then we do a second pattern match on the color and we say well if it's white then we allow this movement and if it's black then we allow this movement. Again this is not about chess. Yeah but what I find interesting is that if you look at this piece of code then almost everything you do is concerned with deconstructing and constructing data structures right? It's almost nothing else. And that's actually what Haskell is ideally about that you design your data types so nicely that coding becomes very like everything falls into place just because you have to construct something of that type. Unambiguous. On line 192 is that the error case? Is that the error on line 188 and it returns you a type position error there or a string? That's very can you repeat the question? Oh the error case it returns this NYI string but you're returning in your function signature a type position. Yeah this is actually lazy I didn't want to program it all out so this is where you throw an exception. Yeah But that works even though it doesn't match your function signature? Yeah it's much like in other languages where you just throw an exception and then you chicken out right? So if you throw an exception you can do it anywhere and it cannot have any type. Yeah so that's just exiting basically. Yeah and then if you run into this it will just basically tell you like something wrong happened in NYI. Can you show the type of error? So that's why it matches. So what that means is that for any choice of a fantasy character? Yeah But what's the word for it then? Yeah I do want to give one on but you don't want to argue that error is more important than data types right? Of course it is because everything in Haskell is over these domains instead of sets exactly because you have bottom and non-permanent right you get defined error without any reference it is error s equals error s and then you have a bottom and you can't get rid of it in a incomplete language Yeah I'm sorry I wasn't just talking out of my ass and I said it's just more fundamental. But what is interesting about the error case is that I had to add it to get rid of this error message I did this warning so now I deleted the error case and then because you have these types with these constructors the compiler actually has a lot of information about the structure of your values and then it can tell you well you didn't tell me what to do in case of a rook and a knight and a queen you did tell me what to do in case of a pawn but before you declare that this can be different stuff and then I didn't want this warning and then I added this case so if you did this right for the other three types you would need the error case So do you always have to put in cases by every case even match every case You don't have to right this is a warning But of course you should take that warning seriously because it tells you that the type checker can't guarantee that you won't get a value of that with that constructor right So what happens if there's no error and you call for it with a rook It also throws an exception that is more general that says like oh there's an un-matched pattern and I don't know what to do Actually more useful than this many cases Yeah more useful than the NYI I really just added that to get rid of the warning I mean we can try that out right we can just go for the pawn which kind of works but we can of course put the rook here and then it will give us this exception Now we remove the error right Yeah so this now is not a compile oh no sorry the first one is the compile time warning and then down here we see exception and it's cut off and there you can see it So it gives you actually a source location and the columns of the lines and non-exhaustive pattern case which of course is much more useful than NYI Yeah that's already the things that I really wanted to talk about There are lots of random thoughts that I have here that we still have time we can go through some of them but so far Thanks for this Anybody is interested in listening more about SUPS So it's quarter past nine I don't know Anybody is half pressed to leave We have Thanks Thanks We're now hiring Haskell programmers Yes Syntax and laying out Haskell code Do you usually do function signature and then the implementation of the function of one to the other? Usually you do, you don't have to but that looks right Would it be an error if you did the implementation say at one, nine, one, eight would that be an error or can you There's a weird syntax I think it's a bit weird where you say where you say actually something like this For example what you could do is say this and then you have the next case here which looks like two definitions but it's not It's one definition of a lot of books It's just a weird way It's a function overloading function overloading of different No, it's not overloading It's the same function That's why it's so confusing That's why I wrote it down with case because then you can see it's one function This is just two lines This actually is semantically equivalent to the case statement where you have one declaration and then a case statement Because in this case if you call a lot of rules it would be line 192 but if you had a point it would fall in one, eight and if you had a queen it would still hold in one, two, eight Yeah, it would behave exactly the same No, it wouldn't fall into one It would fall in one, two Oh It would fall through and then we could You get the same thought as you get with a case So if it was queen you would fall in If you pass it a queen then none of the patterns will match and then it will throw an exception telling you like in the same syntax do these un-match equations Yes Yes, you can And what maybe gives some more intuition for this like if you do this for example then this is not allowed because then it would complain about multiple declarations of allowed moves This is the same like if you if you have it like this this is the same function it's not overloaded or anything Does it make some kind of sugar making problem? Yes, you could very well argue that this is syntactic sugar for a case I think the reason why we have that is that it just allows you to define functions like in a math book or if you write a paper then you may want to write it like that but for practical Haskell, people use that but it's not essential and I think it could be easily if it's only about practical program could be easily from my perspective and made it from language So standard practice using a case instead of multiple People use that syntax but I don't because if I reflect the things the case is easier to deal with People depending sometimes it looks nicer when it's the case and then you may also use eliminate the nested case here because you can put the white in the first equation and the second equation repeats the same but with black in place of color Yeah, you could you could do this, right and then you needed another case where you say black Doesn't make sense that this is the same thing It's because there is a lot of pattern matching or cases matching in Haskell So people do shortcuts to make it nicer Any other questions? I imagine this is a terrible one case You don't have to write keys You can just match this Yes, exactly We have seen a further upright If you have a function like this you wouldn't want to write is valid p equals case p of position x, y, arrow and then you just have one right hand side And I guess Simon also uses that Yeah, of course, in this case No, don't do it It's partially defined functions If you look at a more complicated example further down the problem is if you change anything and you don't have a case statement you may end up changing it on multiple lines If you add a function argument for example, you need to add it four times If you have the case statement you just add it on one time If this is for beginners I would like to stress that you should strive for total function every time If you have a sum type and you write a function over it then you should be able to provide a right hand side for every possible left hand side of your sum type If you have a function where you have one of the possible patterns is something that you don't need to write the right hand side you should regard that very strongly as a code smell that maybe the type you're using is not the right one maybe you're not modeling some of your assumptions on the type level because yeah, because the hassle is not a total language in itself you can write partial function but it's only going to cause problems further down because you will end up using that function somewhere deep down in some other code and you have no idea if the thing you pass to it is going to be one of the things that is not handle by your partial function in some sense this is all this is what static piping is all about if you have the right type then this is the class of problems that you should be able to avoid completely yeah yeah exactly so some of the random thoughts there one of them was that I think when you design a data type for a domain and then you code something with that then you do have to try around a bit to get it right there's often like a first first draft and then one thing that can happen is that you run into a function where you write something and then you realize you don't know what to do with one of the cases and that's exactly what we were talking about and then the compiler errors the warnings really help you and then you rethink can I change the design of my type such that I can get rid of this case for this one function and then you have to switch it around a bit so the goal is that when you design a type or a bunch of types that they model the domain that you have as closely as possible I mean of course you have to allow to encode everything that you need to encode but you should try to disallow as much embedded things that that should happen so an overly simplistic example would be that don't use an in if you're modeling a chess board because you know it's only going to have one of eight possible values right the inside is much larger than just one, two, three four, five, six, seven and eight yes yeah what I also I mean maybe it's not really ascal or specific but what I also really find important is that you shouldn't model anything that you don't need right I mean we've talked mostly about data types and we have done that much with them so that's why we do have a lot of things that we don't really use but that happens in practice too right like that someone tells you to write a chess program and then you say yeah that's good and we have pawns and ropes and board and pieces and then in the end what they want you to do is for example output chess quotes every day right and then you realize well that's we don't need any of the data model that builds so sometimes it feels weird what you can leave out right so for example I would guess that in this data type we could leave out the color of the rook if all we want to do is compute the allowed movements and you don't need to know whether that's black or white yeah you do do you want to catch her? the allowed movements is just like imagine that one rook on a chess board so it's a very friendly chess board it's a very friendly chess board well I mean that's what the allowed move functions like it doesn't even get to take the board into account it just computes the position that you can go to and for that we don't need the color we do need the color for the pawn because that can move in different directions but so what I usually do when I start designing such a data type is what I would actually do is start with something like this which defines a data type that doesn't have any constructors it's called an uninhabited type it doesn't have any values you can construct a value of that but I'm thinking like well as long as I don't know what I want to do with that and what information I want to put in there I just don't put any constructor in there and then at some point I need something to put in there and then I say well maybe this doesn't contain any information right it's just like there's only one value that this type can ever take which is peace but as long as I don't do anything with it I don't put anything inside just to avoid the risk that I put something inside that I end up not using because I wrote it down here Unused constructors and unused fields are dead code too like we usually think of dead code of things that does code that does something but a field that we don't use is equally harmful I wonder if there are some data models that you possibly sometimes you don't want to swipe your time model in the data domain so in Haskell maybe there is for example like commerce, aerospace or different data models already created in Haskell that you could reuse it seemingly well there are lots of libraries that define data types right for certain domains but usually they are quite generic well not always but so for example you could have a library that helps you interact with the Amazon API and then you would have all kinds of data types that allow you to specify the information that you want to send to Amazon and then you get another data type back another value of a certain data type back and then a library would provide these data types that model the inputs and outputs of this to go to the library there is no special repository with only data models that people maintain or standardize only in Haskell yes only in Haskell I also don't know if most could translate data types to any other system of ontologies that would somehow match exactly what Haskell data types can do maybe but I don't know so usually a library is like a mix of functions constants and data types for your domain because the operations that you want to perform on these data types also limit the domain that you want to accept well in those so in Haskell constructors are not really expressive enough to be useful enough on their own right if all you have there is an ADT without functions over it that's not providing much it's not like it's not like you have a rich language about what it means something to be faster it's only business dictionary which you could use not spend the time to model it but yes you don't know what to do with it either you have some functionality mind in which case you will have the function next to the ADT or if you don't but it's not providing much value to have just the ADT because like I said the language of constructor is not rich enough to encode anything non-trivial I mean if you look at it from the curry hour it ends but I mean we have a library that types HTTP HTTP types we have a library that finds types or HTTP mostly only types I mean there are some tools that actually look at some for example Miha wrote a tool that looks at JSON and then generates Haskell code that is data types for whatever however the JSON looks like but then as you said you can use something with that just the pure data type definition is useful but not a pro and in practice that is a very uncommon case like most of the times we were right there the data types by hand so like in Java in other environments you have some data models for pet industry they are standardized and so on sometimes it is useful but you need to have some default logic as you said I was only interested if you have something in Haskell so yeah, thanks I mean of course we do have something like libraries like data structures and that type but again together with the operations in Java if you get a clause then you are getting type definition and some method so in Haskell that would be getting some type definition plus functions but in Java you don't need to have the methods you want some abstraction to talk to the business user analyst and so on so you don't want to waste the time to analyze the business domain because some business don't make a long e-commerce banking they are quite standardized good question notion of extension of existing some type with more cases not in the simple algebraic data types for this you need to to capture the demand that seems to be here at some point his name is Lawrence and he has been working on data types a la carte in constructing data types together all making sure that your functions work for this data type and the other data types that are kind of derived from it but if he's not here I will not repeat his lecture because it's quite immediate quite long the technical terms closed the data types are closed you cannot after the fact add anything even if you have to also as the new alternative every function maybe a new type same some cases but maybe more cases but if it's a new type then you can easily create a new type or one of the alternative some product types where the first alternative just embels this existing type the second one is your first new constructor the third one is your second new constructor that's true I also wrote down something combinatorics like when you have this sum of products you can think about what is possible with this system so what you could have is a wrapper type that is just one constructor and one field it cannot possibly have more information than the type that the field has but it's a different type so the compiler can tell the difference then of course you could add another constructor to say sometimes I don't have the spell you have something else but then you cannot use the same selectors you have to always unwrap what you have wrapped that's usually intentional because if you wrap something you can mean that maybe it has some special meaning to not just every process ID just process ID that I produced in my program for example or not just every sockets just HTTP sockets so the information underneath is the same but the way you use it is different maybe you could also talk about the type for the wrapper I don't think it's such an important concept like this so these wrapper types what I'm thinking of is something like this or maybe we use something like minute what this tells you is there are not more minute values than there are int values but it has the compiler and the programmer that this is a minute and not something and then you can always use new type which changes I think rather are irrelevant things so basically new type means that it has to be a wrapper type yeah it is strict and you know where but so you can use a new type whenever you have one constructor and one field and then it's slightly faster and it's strict what I also find interesting are enumeration types so what you could do is you have certain flags for something and then you can have you know what I think Java they have enumeration types and in C you would then use enum and then you can just simulate that by some type where every constructor has no fields right and then one thing that might be interesting is why is it named some types and product types so the names come from I mean the product type of course there's the Trancartesian product but what I'm always thinking is that if you think about how many possible values a type has than in a product type for example if you have a product type of color yeah like this well then let's start with color right so this is some type and every constructor has only one value right like white can be one value and black can be one value and the number of possible values for color is the sum of these things right there's two possible values if we had another constructor then we would have to add that right and if we have a product type that takes two colors then the number of possible values of that constructor is actually the product of the possible values of our fields so this case would be four does that make sense I think if there are no further questions so again I encourage people