 So maybe I will do it like this, yeah So I Try to make a presentation with Tim Oxley about type classes that seem most difficult for beginners. We had the certain stumbling blocks in particular the fact that I got MC today and Tim got wedding two weeks ago But at least what is there to present I will present and if There is anything that feels unfinished. Please forgive me and there will be certainly an opportunity next time for me to improve on that certainly for all the Haskell code in here is fault of mine not Tim so you can complain to me directly and and Yeah, Tim was very very helpful in finding what is the most difficult and what are the Difficult things that we might want to talk about. I'm afraid that I kind of trimmed the topic because I removed the Common hotspot for example, I decided that it feels too much unfinished So let's try So first the goal of this talk is to give some intuition behind how type classes work and what they are useful for If you are not real Haskell are like hundred percent of your time It may be a useful way to learn certain design patterns that are ubiquitous in functional programming And if you are real real beginners, we will Tell that monads are not scary Basically, the things that monads cover usually in this Haskell code Are much more scary and I will give examples of that so the First thing and the most important why do we need type classes? So basically if this is for the this is 101 this slide and few following just for these people that Did not yet use type classes a lot. So for example, we can define addition on integers as a plus operator that takes integer Takes the second integer and returns another integer But then we also want to have addition on doubles So we take two doubles and we return a double and The most natural solution would be to say that for any numeric class Or any numeric type alpha Or a here we take two values of this type and return the same time And this is solution that is successful used in many languages like You see this operator plus in C plus plus you have similar construct in small talk But in Haskell we want to type it strongly and to make sure that it doesn't bolt so we want to have unique semantics at compile time and And the solution is to say exactly this that this type class is constrained is satisfied that you have Type alpha that is of certain class of types another Way of saying that is that it's conditioned on a type. So if we have a type That has equalities that we can compute equality easily on this type We mark it as EQ a and We can use a quality operator that returns pulling in It's also a hierarchy of properties because some type classes They have to be subclasses of the other classes. So this hierarchy is Basically the same as in object-oriented languages, but at the type level so it always applies to type in In object-oriented languages, usually you have hierarchy on the object level. So, you know that object is also instance of The the super class And that's how you define the semantics here We say the type is instance of a certain class and the classes are Related to each other somehow It's a type safe a correspondent of virtual method and call me hi mechanism also And it's the most common way of expressing design patterns Besides higher-order functions So most important type classes probably are monads which expresses the general general sequencing computation along with monad transformers Functors applicative foldables Traversables foldables and traversables with skip for the other talk and to start with the hardest We have a definition of the monad according to philosophy of Leibniz They are indifisible and hence you ultimately simple entities Such as an atom or a person. So you cannot just split the person in two halves and Say they are they are two different people. Yeah So if you start splitting you will subtract something inherently and this is actually the intuition how we how we use IO class in a way in In Haskell that we cannot Look into computation that is expressed by IO Without breaking something. We just can execute it and they are the general problem to problem to Generals solution to the problems of sequencing in particular unsynchronous IO computation or exception Handling as I show Now so the simple JavaScript example just in case anybody of you use this JavaScript It's kind of little short because normally you have these multiple nestings as as many as ten of them and It basically looks in this way that first you read file a and then when you read this file you attach the call back to the function that Takes an argument and reads another file and Possibly error and then you concatenate these files and do something else with them so basically you have to change the callbacks and You often reach something that is called callback hell if you search on the internet for callback help you will see a lot of examples and Unfortunately for some reason I think I just broke this presentation so So I will I will fix it on this post Which is very very bad that the list will see it. I think the presentation was just assuming that You have a bigger projector No, not this one. Oh, so it should be this one. So in Haskell you can express it By basically using grid file function and then binding the result Which is a little bit easier to read Even without do notation and As Omar already told the the idea of the monad classes that you have this return operator That injects any value into the monad and you bind two monadic operations. The first is is normal operation that returns The value of type alpha and the second takes the value of type alpha and returns the the action or monadic value of Something that returns value of type beta and you can compose them. There is also alternative formulation that allows you to use for join function Either of them can be easily translated and this is my favorite example of of how You don't program but unfortunately, it's very common that you have to program like this in C So we have a simple C program that opens file checks for errors then reads a buffer of the size of four kilobytes Then checks for the errors during the reading Then it follows with checking that the header is Not too small after passing the header Each time we check for error. We have to add another level of if then else until at the end after checking headers numbered of rows and The fact that the parsing is complete. We can return the result So we can say that there is a common pattern here after each operation We need to check for errors and if there is error we need to basically abort all the other operations You can do it using go to but Dykstra would shun at it So maybe we would like to have a structured way of saying that after every operation We checked whether there we have a valid result and we continue to with the following computation and This is how you do it with explicit binds. So basically you say that you have Some left value that is exception or right value that is correct and you can Bind the functions together in a monad with a do notation. It's even simpler. You simply simply finish with the adding the catch on the top level and and using run either which is either monad and Then you it seems quite natural You just execute following actions and make sure that they return either Right value or left error So that's called monad either from the either type that is used there You have to fail over there the fail is Not the error from the fail is not captured by that system So actually for monad either it depends of the formulation we we hear we use the formulation that fail is corresponding to left The return left so and the implementation is pretty pretty simple We just stack either on top of some other monad in this case may be IO and We use the left a for error and right B for the correct result And basically each time that we execute two following actions. We check that is right value and Then we continue or if it is an error. We just ignore the following part of the a similar type class that is ubiquitously used is Functor and In the simplest idea it's some box that holds the values of type alpha But it can also hold values of any other type So the basic operation that is functorial in the cantorita cal manner would be applying the Some morphism or function that here is a first parameter That transforms The things of type alpha into things of type beta and this is basically how F map works The applicative is functor that has the zipping operation So it can use the function and apply it to every member Of the functor or everything that is in the box the simple example is the Applicative on list where we apply the function the functions in the list to To everything that is on the list Another one that is very useful is category Which is something that looks Like a function, but it's not so it's basically anything that can be composed so it has to have identity object that goes from a type a to type a and also Can be composed and Simplest example of category is lenses Which basically go from type a to type b and they are almost like a function So the get is basically the function part of the category instance in here But you can also go in the other way so if you have the Object beta and object alpha, then you construct Back the object alpha with its fragment changed and how I would use it is mostly for time representations or something similar so you can just Define it daytime time and hours Lenses and the daytime goes from Daytime with time zone into daytime and so on and then whenever you want to set the hour as integer within daytime with time zone you basically Set and then compose the lenses behind it arrow is Another useful class, which is also generalization of function So it's in a way like category that you can compose it Then you also have something similar as in the In the monad that you can inject any function Using pure into an arrow. So if you have a function from in to out We can inject it to an arrow as As arrow from in to out The second thing is that any two arrows can be composed as Arrow on on a tuple Basically, so that means that any Cartesian product you have in the arrow and The last thing is that you can always split That's for any any arrow from input you can and the other arrow from the same input you can basically use them both on on the given input and get the Product of the results There is also a small part, which is that in the arrow You can drop any part of this Cartesian product that allows you to basically express the generic Dataflow computation or computation graph and that was kind of very very very fast demonstration So the questions complaints Oh Yeah, it was okay. So yeah, maybe I should tell more detail But the idea is very simple that in each of these cases you have a design pattern that uses pure operators You find pure operators together few functions That are useful on many different types Like f-map, yeah, so you can use f-map on lists of anything you can use f-map on IO So it's basically a design pattern in each of these cases and I'm not sure why it doesn't this this formulation of design pattern have their reputation of being complicated Because they the underlying concepts are in a way quite simple So arrow is anything that can be Constructed from the function. Yeah, so you have a simple function and you construct the arrow Category is anything that can be composed Basically, so they are like functions, but maybe not you cannot simply construct them from functions Because in case of the lenses, they are bidirectional Yeah, so we can always set and get from the given value. They are also there are also isomorphic Lenses iso lenses that I have dissimilar property. They are like category with inverse operation I mean if you if you use the the design pattern of say the Command pattern in object oriented languages. I'm not sure if you've yet about it It's very similar to to free monad basically So you can pretty much translate any of the object oriented pattern into type class in Haskell or most of them Except for callable pattern because we already have that we can basically use higher the functions Can you give me an example of instance of arrow? So of course the simplest instance of arrow that people actually Can use is is the function itself. It's like identity arrow So there is nothing but the second thing is actually you can express parser combinators as arrows as I said data flow computation is Like the simplest example that is actually useful. So it basically make a arbitrary data flow or a VHDL computation and then if you can run it as synchronously If it's scientific tree why so spreadsheet is it such an example. Yeah Yeah Well for parsing this wouldn't work because they If you consider how normally it's formulated as an arrow You still have this sequentiality. So it depends whether it's on the left side Or on the right side of the expression and depending of this you have different ordering of the expressions But it it's useful for data flow computation where you say that it doesn't matter. So it's commutative Yeah, so basically commutative arrow is something like simple and Yes, because The advantage of using this formulation instead of monad is that you have explicit resource control So we have to drop something out of the confusion product whenever you don't need it And you know where you drop it and in the monad when you bind something in theory It's up to the compiler to optimize out that you will not ever use it again Yeah, so basically if you look at the bind here, so it basically tells that This function mb error Can have any long-term dependency on the previous one. Yeah so it can Use it long long after it has been passed and this is the weakness Yeah There is also maybe that that's not kind of sideways because I aim to like make the the basics type classes Simple and but there is kind of advanced time type class Set of type classes actually they call are called the generalized arrows And there is patch for GHC compiler that basically translates translates any lambda expression Into reshuffling something That it looks very similar to arrows with few more operators To have explicit resource control there. So at this moment Our sack or a force more than simple stuff so you mean for the So there is a big progress in limiting resource usage in case of I think out of person and Like honestly if you have resource problems Yeah, we're possibly using arrows is it's better idea, but But honestly, I'm actually considering rewriting my own parcel combinator library because I I have a lot of ideas in this regard but If I have if I didn't have time to write my own because usually if you have a big project like needs More of the parsers then you can schedule a one or two weeks just for making Parcel combinator library, but if I didn't have time for it I would probably use use actually Monadic parcel combinator, but this is only because they are simplest to use Yeah, so arrows arrows have a lot of advantages, but being simple to use is not one of them Unless you are really into data flow computations, but I know about people that prefer to construct graphs arbitrary graph expressions or data flow expressions Using special kind of monad that is basically deeply embedded DSL Because monads Monadic syntax in maybe monads are not simple, but monadic syntax is in Haskell makes it much simpler to operate on a unique note Yeah Polymorphic on the first parameter of what? Here Yes, there is there is one one very simple answer fail is defined as Taking the string argument and and I want to redefine fail. I do not mention it there That that's the only reason if I really think the fail should be defined as Taking exception argument or something of the that is instance of class error and That will solve the issue But as of now fail in the monad the first the problem is the fail is defined in the monad and not in Yeah It is actually very important because what what it gives you is that every pattern matching that fails will be Silently converted by the compiler into code to fail on the missing case, which means that you can do pattern matching Control in a way in your monad in a meaningful way Without raising any exception or anything Yes, for example Yeah, that's pattern matching only in monad divine, but it's it's in a way I think there is only small thing missing from this and and having nearly nearly like complete Abstract syntax for for for shallowly embedded DSL's Where you basically do pattern matching in a variant of the monad and the other variant basically sequences the computation and The the the alternative way of formulating it then you probably have to do something as complex as generalized arrows that I mentioned before