 How many of you are familiar with a lot of function programming concepts, or how many of you are here as beginners and want to know more about it, just a show of hands, if you have ever coded in any function language before? All right, so my talk is probably aimed at someone very intermediate, I wouldn't call it a beginner talk, not an advanced talk, so let's get on with it. Okay, so the topic of my thing is about algebraic data types, so I'll be delving into what that means and also what are the other things that you can do with it and what are the other programming paradigms that we have to use these types properly. So before I begin, for me it was a purchase part that got me into the world of function programming and then I moved on to learning Scala, so I've been coding in Scala since. Yeah, so I work at a company called MediaIQ, it's a NAT tech company. I work with their data processing team, so what I do there is basically we have this whole ecosystem where we have different microservices for a lot of different machine learning and other data processing tasks, yeah, so I work across these processes and so because of it there's a lot of common things and a lot of things have been made easier for me by using an ADT and other concepts that I'll be talking about. All right, so I'll just begin with the most basic question, like who can tell me like a proper definition for a type or what does a type mean to you? Okay, so I'll just start, so a type is something that you represent your domain objects and stuff but it's not exactly like a class, it is, I'll be going through that. So we have, okay, so we start off with the primitive types that we have in most languages, so we have like a Boolean which has true or false defined, you have integers and strings which have, it's not in an exhaustive but it's a huge set of things that correspond to a string and an end, right? So the thing with type is like a distinction between a type and a class is you can see a lot of, I mean you can see it in these examples where types can be equivalent to each other like a string is always a string but it's only when you wrap it around something like a class that string gets a different meaning and you have all your, you need to redefine your equal and other operators, right? So this is a simple example, say you have a function that takes in, like that converts something for you and if you just take in like a double here, what you can see is now I may send a distance in double but I can also, I mean if I have other values that are also represented in the same things, it's going to be an absurd computation for me, like in this case it may not break it but there may be cases where it might be an invalid value for this one but it's accepted by the function nevertheless. So these are, I mean this is a very basic example but so this is the reason we usually use classes to avoid such things. So with a proper type, I mean so an example of that would be to just convert those things into their own classes. So essentially that's what we refer to as a type in this example. This is just an example to show that Scala has this other feature called value classes where if you just want to put one parameter attribute into a class, it does not incur any overheads of creating the class. Like at the compiler level it is still a string or it's still a double or whatever the type is but for you, you can access it like a class. It actually gets implemented, instantiated only when you do other things like pattern matching and some other things but in this example, this is fine. So this is just a quick intro on what it means to say that it's a type. So you also have a bunch of other things like coming from an OOP world, you could think of interfaces and inheritance and things like that. So something similar to that, there's a lot of functionality that you can extract from something called type variants. So okay, so this is an example domain that we have like animal or dog is an animal and a puppy is a dog. So there are three types of type variances in most languages. So the first one is invariant, that is if I say that I expect a type T, I'll only process a type T. So in this case, if I create a zoo that only accepts, I mean, if I create a dog zoo, it'll only accept elements of those things. So what you can extend, so that is what is invariant. Like any other type I try to put, it will not go in, based on whatever function that I have inside that. So covariant is something that can accept that type and it's superclasses and it's subclasses. And Contra is the other way around where class A and it's superclasses. So that's essentially what these three terms mean here. All right, moving on. Okay, so yeah, coming to the algebra part of my topic. Okay, so I'll just quickly run you through what some of the terms that you deal with in functions, right? So typically you can represent a function in this format. So what we call a domain is, what are all the possible values that A can take? And what are a co-domain would be, what are all the things that your output, that is B will take in this case? And range is, it depends on function to function. So why I'm talking about cardinality here is, will become clearer later. So given a function, you can, if you wanted to compute the number of different possible, you may not, the number of different possible inputs and outputs that you can have is B raised to A, where B would be the cardinality of your codominate, so this is the basic math that we learned in school. So you can also extend it to things like, right? Like when you start adding more types to it, you can just simplify. So this equation essentially is just to give you an idea of how you represent something in an algebraic world and how that maps out to in the way you write a function in the code. So you can see that these two terms are equivalent. So like C raised to A, I mean, we can break it down to any way we want and that corresponds to the kind of function. So this in function programming is a concept called curring of function. It's quite commonly used in a lot of areas, right? I mean, depending on your use case. So coming back to the cardinality of it, right? So why this is important is, this gives you an idea of what are all the different outputs. So if you think of the mathematical part of algebra, like the one thing that stands out to me as compared to like in maths and especially in algebra as compared to other fields is that there's this, there's this almost totality about the way these laws have been defined. Like I think for centuries nobody has tampered with any of these laws. You never hear about someone disproving any algebraic law, right? So that's the idea behind this, that you kind of want to define your code in a way that it is total and it handles all the possible cases. So we start off with like by just trying to count the number of types and all that, so this is a basic stuff, right? So now, once you start extending those things to your functions, you start to get an idea that there's a lot of, like there's a potentially a lot of different cases that you can come across in your things and any one of these could be a bug that you need to handle, right? So and so the notion of cardinality is something that the compiler also doesn't necessarily calculate, but it helps for the compiler to know that, okay, I'm going to get these many types of, I mean I'm going to have to handle these many different types. Which is why we typically, instead of using a string, like strings for most use cases is fine, but strings are discouraged for this reason that if you can possibly put it in an enum, you put it in an enum, not leave it as a string. Simply because of just the performance again and that, and mainly that the compiler knows that this is what I need to accept. I don't need to go through all the other cases and stuff. So this, okay, so this is the basic thing that we have, right? So coming to algebraic data types. So they have primarily there are two types, product types and some types and there are other derived, I mean other types like a unit type, there's a recursive type and hybrid which are all kind of derived from these tools. So I'll start with what's a product type. So product type is essentially saying give me A and B. Like if you have two types, give me A and B. So what you typically see that is in tuples or classes are just like a projection on top of a tuple and they have different names but they are the same thing. So products are probably the things that we are most commonly, I mean that we are most familiar with. Like I mean in a mathematical form you would represent it in a way that you have a bunch of types and you're creating a constructor to be able to use that type. Like for example, a simple product type would be a pair or it's just a tuple two, right? Similarly you can also extend those things for your normal classes, right? So the thing that I mentioned earlier, like to a type system, a pair, A, B and an employee in this case would definitely be seen as the same thing. I mean it's equivalent, it's not the same thing, right? So this is something that is exploited a lot by libraries like A plus to be able to do degenerate programming, right? So I'll be touching upon that later, right? So simple stuff for those not familiar with the Scala syntax, that this is how you define classes here. Similarly you have some type which is basically saying give me either type A or give me type B. It's like enum is the most basic example of that where you say that enum of this type can either like a day of the week can be in a Sunday to Saturday, right? One of those values in the functional word and either is probably a common enough some type that you might have come across. It's actually a hybrid type because it's a mix of some and a product type. So for example, you can see that either projects to a left, I mean it either maps to a left projection or a right projection depending on what value it contains, a simple enough example if you wanted to create your own would be something like this, this is usually the way you define an enum in Scala, right? So this is how you would write a some type and now when you want to combine both these some types and the product types, you end up with things like these where, yeah, I mean so this is just a combination of it. So when you use it, you can have function that can be generic enough to accept your primary type, but also you can have specific modify, I mean, like any other parametric handling. So yeah, so one useful example here would be how a simple tree would be represented in using the same approach. So you define a general tree saying that it's going to be a tree accepting any type, right? Now you define a unit type on it, which is this empty object here. So you say that this, yeah, okay. Similarly, you also create a leaf on a node. So this entirely is your algebraic data type for representing a tree in this case. So there's a bit of recursion involved here as well like a node can do things like that. So now on top of this, if we were to write a simple function, say whether an element exists in a tree or not, the way we would do that is by using pattern matching across these three classes that we have defined. And accordingly, define this behavior. So yeah, so this is kind of how you work around with algebraic data types. And I mean, later on, so you can add a lot of, so the thing about these functions are, the emphasis is on writing them, like handling the most generic cases. And then using the primitive things that you've defined to club them and make it generic enough to handle all cases. So yeah, so coming back to the algebraic part of it. So if you want to understand commutative in terms of code, think of it as some, think of it like an operator that you have defined. Where in you have defined a combiner and you've taken two types of those and you need to return a third type. Could anybody tell me what this is in category theory, what is this called? Like something that has a combined operator defined on it. No, this would be a semi-group. So a semi-group is something that has a combined defined on it. Okay, so given elements of, so basically it has this commutativity and by effect, associativity is also directly derived from this, as you can see here. Like I just need to do something like this to get these two properties. So this is what a semi-group will satisfy. Okay, and then coming to, I mean distributivity is also, distributivity is again derived from what you already have. Like it's encoded in the definition of an algebraic type itself. I'll just load the whole example here. Yeah, so basically you can think of factoring out common entities as a form of distributed operation like in this example. Coming to the next one, so this is, okay, so tell me if something has an identity operator, has an identity object defined on it, which essentially in multiplication, it's basically multiply that and you get the whole thing or in addition, basically you get back what you gave, right? So this is what is called a, this is what is a monoid. Something that has a monoid as a semi-group with this identity operator, identity element defined here, like when dealing with options, like if you ever tried it with an option or any other things. You do a flatten on it, what you do is it'll combine your sum and none and you'll only be left with your original list and things like that. So that's essentially the monoid at work. Inverse is another thing that it's rare to have inverse in some most cases. But yeah, inverse is another thing that we have from our mathematical world. So that's just the same thing that you multiply your type and the inverse of the type and you get your identity element. Could someone tell me what this would be called? This would be a group, rings I will have to confirm once. Rings I'm not sure, but this is what is defined as a group in this case. So these are some algebraic terms, it just had to throw them here. Okay, so right, so now explain what these types are. Now, how do we actually use, how do we actually put them to use, right? So type classes are the standard ways of doing this. Okay, so firstly, it's not the same as an interface. Why I say that is when you define an interface, okay. Since I don't have much time, the shortest way I can explain the difference between a type class and interface is interface is something like the object is born with those functions with that ability to perform something. A type class is something that you teach it over. I mean, it's something that you feed it to it later on and it learns, that's the bare minimum definition. So one example of such a thing would be like, say you have two classes to find and you have a type class called to introduce some kind of a behavior. So what you do is you outside the scope of your object, you define the functionality to it. And then whenever you need it, you use that functionality. So it's useful in writing libraries as well where you can keep adding. You don't need to keep changing your original code and stuff. And what more you can do with this is, so you can use a, Scala has a feature of implicit. So a lot of times you don't even need to explicitly do these things. So when you load certain libraries, some things just start working out of the book, you don't do anything. Okay, quick run through on some of the, I think you already had a session on this one, right, I'll skip through this. Okay, so if I have time, okay, so I just want to have, I just want to share, like spend some few minutes on how this actually relates to the work that I had been doing. Okay, so I've just taken a small example. It's essentially, it's been stripped down to a basic JSON parser, right? So just think of this as a JSON parser. So you have a base trade and you define some of the primitive things as the classes that you want. So what I want to do is to be able to take any class and be able to be represented into these forms. So that I can do any some form of optimization. So whatever, whatever domain logic that I want to use on it. Similar to a JSON, where you encode that particular string and then you put it into a JSON value object and do something like that. So in my case, that's a param value, right? So I define an encoder to do the encoding for me. So what this type class tells me is if I can, if I can find something that is going to encode a type T into what I want. Then if I find that implicitly, I'll use that and do the encoding for me. So the idea here is now if I, so here I define it for string and int here, since these are the two types that I have. So the idea here is now you've done it for string and int. Say I add one more to be able to do the same encoding on a list as well. So then what that gives me is now you give me any list of string and int, any combination of list and ints. And it should be able to encode it automatically. Like I don't have to write custom encoders for each and every object that I deal with. That's the idea behind type classes. And it's also less maintain, I mean, it's less worry about maintaining. Because it's just a single point that you need to worry about. Let's go next. Right, so I just create one sample class of how it'll be. So initially, I would create a custom encoder in such a fashion. So what I want to do is I want to encode the keys and the values along with it. So if I have to write it manually, this is the way it would look. But where we can take this further ahead is by using, okay, by using generics. So I'll talk about generics if I have time. Just one quick thing I just wanted to show. So if anybody uses Scala, there's this library called Scala check that lets you write property-based test codes. What that is essentially is, for this example, it looks like this, where it generates a bunch of possible inputs that you need to deal with. And you can write your classes in a form of defining these properties that for like for a fraction, your denominator should be positive and numerator or whatever, something like that. You can define all these properties that typically define your domain behavior. And it generates a bunch of different inputs and it gives you all these details that, okay, for this input your property didn't hold. So it really speeds up the way you do your testing. And it's also less test code to write. You don't have to think about inputs and write multiple test cases. Yeah, on last minute. Okay, okay, I don't have time to explain a lot of this. But yeah, so whatever was defined there. So now by using, so I'm using this library called Shapeless. So what that lets me do is, in essence, this is what it lets me do. So this case that you see. So if I can define it for a small slice, and if I define how to extend that slice, then you give me any class. I should be able to encode my object into it. So essentially that's what Shapeless does. So then the code that I showed earlier where I had to write my own encoders manually, that doesn't have to be done anymore. As with any JSON library that you have ever used, they always come with these automatic encoders. So yeah, this was my talk. I just wanted to show you what are the different ways you can use these, use this idea of an abstract, of an algebraic data type in your code. Yeah, that's about it. So any questions? Cool, thanks for coming.