scala.bythebay.io: Adil Akhter, The Essence of Functional Structures in Scala
Recording: scala.bythebay.io: Adil Akhter, The Essence of Functional Structures in Scala
you good morning thanks for joining this talk today in this talk we're going to take a look at some functional abstraction the idea of next 20 minute is too yeah we'll cover some cool concept of functional programming first and then we'll take a look at some function on obsession to see how we can use it to solve our problems in a purely functional manner so before we get started note that i'll be using scholars at seven point two point two for the rest of the presentation and yeah let's get started so if you take a look at functional programming what's the court building block of functional programming it's just functions and somehow perhaps that's the only abstraction you need to write a program in a modular and compositional way not a method not a class just a function but when you refer to the term function what do you mean by that we mean the mathematical notion of total function a mathematical notion of total function is defined as a mapping from a set of value of type a to set a value of type B such that each element from said a is associated with exactly one element in set B so mathematically a and B are also called domain and codomain respectively so scala far as a functional programming language we use types to represent this domain and codomain you can think of a type as a component description of a set of values using our functional paradigm of scala functions are nothing but just a values so you can pass a function as an argument or return function as a result and this notion is called higher-order function we can use higher order function to compose functions together in a compositional way so in this case I have a function from A to B and I have other function G from B to C we can compose this function together using and then come and that's just one combinator but perhaps you can use some other communities as well to combine functions together so in this manner we can write code data compositional and we can do equation or reasoning as well so that brings us to the design philosophy that I like to cover in this talk that we should always write total functions so if you take a look at the example shown there the recording it is a function that's taking some values from console and trying to convert it to double so if I ask you this question that okay what's wrong with this function easy the function according to the definition we just had the if you take a look at the type signature the type signature of this function is from you need to coordinate but if I do that take a look at this function given that the string that we are reading and personally to double in this thing is not a valid double then it will throw exception at runtime so that's kind of insanity right e exception when it's thrown and does it's not capture in the type signature so following the type i have no way of doing that okay it might throw an exception and also that it it breaks the referential composite referential transparency of functional programming paradigm we'll take a look at this example later with a functional abstraction that would be explaining in the rest of the top but just to give you a simple example of the similar problem we have a function divided hundred by integer so is it a total function absolutely not for value zero it might throw a medic exception at runtime so how can we solve this and make it a total function there are two ways that we can do that the first way of doing it is to restrict out to me so for instance we are taking Excel argument we can come up with a new type called non 0 int which restricts the domain further so that there is no value 0 in the in the domain and we can use it as a total function now and the second way of doing it is to use some other return type for instance here we are using option of double so this this type option of double as a Kudo may represent the notion of optionality that's one way of doing it in Scala but you can you could use some other algebraic structures to capture the notion that this function be right-handed by might not have an answer to your computation so give an x equals zero it will return none and for other X other value of x it might give you the result of that computation so this is how the option we can define an option so note that here we are lifting a value a to a structure so this this this notion can be further generalized for other kind of container types so this content type will will have a value a but for instance we can have a a which is a continent type that can contain a value of a so a typical example of it could be list option you could think of this as computation as well that may hold or run forever like task or future so as soon as we start using this kind of abstraction in our function the problem become that okay in order to apply a function to the value that container has we need to first unwrap it then apply the function then wrap it back it's kind of inconvenient right so this kind of containers they often use this function on Mac which allow you to apply a function to each element of the structure so like like in this example we have option that has a map this has a map try also has a map and this kind of parameter is time this notion of map is not specific to optional list but we can abstract this concert too wide variety or parameterize types and this idea is captured by this algebraic structure called ponter fun to abstract the idea of mapping a function over each element of a structure so in order for H to be a functor it needs to implement this operation called map but that's only part of the definition the second part of the definition is that in order to be it to be a fighter it needs to also satisfy some loss so that's the cool thing about functional structure it cannot come up with a with a structure and also it is associated with few laws so typical example of fun tour is released so we can think of the functor is released so if you take a look it must satisfy at least these two property one is identity property and the second one is composition property the composition for the property refers to the fact that if we map heir to the functor and then map G the result should be equal to if we apply the composition function f of G to the factor so anything if we it has to be a functor it must satisfy at least these two laws if we consider Syd Syd doesn't satisfy this composition on zillow I'll keep it as a homework for you guys to figure out why that is not a hunter due to shortage of time I'll just yeah I start explaining the other stuff that I have to say so just to give you an example here we have a binary tree and we can create a functor for this abstraction quite easily we have to use a map function in case of leaf we are trying to apply a function f a to b to it and if it is a node then we're traversing the lab tree and the right subtree so if we see it this implementation we can create any tree with the setup screen the tree that contain your screen and we can apply this function f to it and normalize the content of the tree tree of the screen so one thing to note here is that the font earth that applies a function that only take one argument but if we want to generalize this notion where we want to apply a function with multiple argument we need a bit more power for that section which is applicative it's quite a powerful abstraction the idea of applicative is too allow a function with number of argument to be mapped unlike functor which only allow a single argument to map applicative has this generalization in order to be a optically a functor must satisfy this hunger must implement these two operation apply and pure so what is for instance pure pure has the ability to confer convert any type a to a functor so sometimes we refer to this operation as lifting and apply has this power of applying a lipid function a 2d to fat32 FB so using this mechanism what we can do let's take a look there three options and we have this operation some that's taking three integer and some lemma so we can leave this function in applicative context first and we can apply the function partially to the option that we had and the interesting fact here is that we are partially applying the lifted function to a little valley so it gives some interesting observation from this thing is that we are applying function on arguments but this argument may be a side effect it may contain some kind of effect so it generalizes some notion of programming with effects so we started with the fact that we would like to generalize this application of function so that applicative allow applying functioning multiple argument to a structure to tree structure and elements of that structure but somehow we ended up with a much more generalization there a generalized view of applying pure function in effect cool arguments so that's the district or essence of applicative there so to sum up we saw hunter that allow mapping a function to each element of a structure and we saw applicative applicative all load yah was to generalize mapping function with multiple argument there is more abstract to you that is programming with effect but if you note here we are only able to apply a pure function in a factual argument but what if we want to apply a ethical function then we need a bit more partial obstruction and that is Bolin monad captures the similar notion of effective programming as applicative but applicated style has some restriction that it only allow us to apply pure function whereas am honored generalizes the notion of composing function with effects so in order to be Mona it needs to implement single operation called bind vine has the ability to extend the computation fa with the second argument there a from A to F be so this is all buying and also flat time in Scala because the second argument there binds to the result of the first argument fa in case of the fact that if a failed it propagate error throughout the chain but if FA is successful then it apply the function a FB there so easier does not have to do the exceptional moving by itself it's given by default by applicative sorry bye-bye this bind function so to keep you an example recall we had this function read coordinate which was reading some some screening and phone running into trouble longitude and latitude and then it's creating this coordinate object we can use this kind of case class I Oh to describe this computation so in this example we'll be showing that how we can refactor the previous effect will go to using mana to pure function so we can we can have this case flat I oh that this that can describe some unsafe operation and we can describe the red line and print line function that we have in the previous function with this IO abstraction so basically here instead of doing the eye operation immediately we are describing that what needs to be done so we are describing computation as data we can generalize this notion further with this polymorphic function io which takes any kind of computation and lifted to the io context so we can define a moment for this I Oh in case of it you wondering the point function it's just licking a into IO of a using the using the generic function that we have defined earlier at the second function binding its binding the first operation fa with the second argument f so first is invoking the first operation and then if the operation is successful then it's applying f and calling unsafe are from IO onda on the outcome of F so exchanging to operation together in that way we can change our sequence of operation like first one is printing something and reading online and second ones printing latitude and reading a latitude value from the console and returning an IO of string to string so note that now we have a function signature from unit to iOS furniture at that point we haven't done any i/o we just say that okay look we can describe this i/o operation and will be executing it when we need to execute it so we just described the computation as data at that level now when we call this read io function dot unsafe part for my oh it will start executing the whole description the chain so this way we can we can abstract side effects from our vehicle and delaying the side effect as well so given this way of chaining I operation we can extend this example and read for instance latitude and longitude what we're doing we're reading it as double we're trying to convert it to double and if it fails then it will return none if you successful then we're creating an option of that value that's all that's one way of solving this problem but you could you could use some other abstraction as weather like either or something else so we're reading latitude and along with you we're not reading we're basically describing that we like to read along with you than a latitude and then we are combining them with the applicative billers there so basically we have to option and from the two options we're creating the coordinate object and I'm returning we are returning the accordion object that we have we have created at that point so in this example what we did we describe several operations several I operation that needs to be done we haven't performed any i/o but next when you call unsafe perform I oh it will start changing those operation and start executing so given you provided to the value of longitude latitude is a correct double then it will it can convert it to coordinate object there but it's written some accordingly but given that you enter a wrong input there it will provide you none so now at the function signature its encoded that ok given anything you will be getting an IO of something and I Oh option or IO of none there so in this manner you can you can app you can isolate your side effect from your pure code so if you have a few code and impure code and at the end of your call graph that you were performing the side effect there so just recap we had functor we had applicative and malad applicator and monitor is bit interesting it allows you to do affect full computation so functional programming we think of it as a lack of fire but that's not true it allows you to do affect full computation with the applicative and moment it has the whole framework for you to do right that happy flow and rest of the exception handling is done by this bind operation but note that here we are only doing the structure preserving transformation basically we are transforming afraid it'd be what if you want to transform f it to something else then you need a complete different kind of structures that are mo light and friends so I'll quickly go to go through them amyloid is a mathematical concept which has only two properties first one that it has a zero element and then it has a append function that can take 2 item on the same type and create one item from it so it can reduce right into one so in this example we have a monoid that happened four and six and return ten-four by combining them together we can use this structure monoi in volleyball to to combine all the values of a structure into a single value so it has this function called format it's more like this that ok you can map as a more like making a sandwich here you can you can map a structure to ma noi Dean sense of it and then you can reduce it so you're mapping and falling that that's that's how the for map is defined so you can see amyloid instance there so that that's their that due to the fact that ok you might have something that is not usable so you you are converting these objects do something that is amyloid and when you have mon right you immediately have this review from reduced function there so you can produce more this thing to assign users that's amazing it so we can write much more generic function with with this kind of structures for instance we can write a generic format the restriction here is that that if has to be affordable and it has to be more right then almost every kind of structure for every kind of structure this volleyball and monoid is defined for you and then you can call this sum function we can sum up a list of integer a list of option even a tree of option together so this way you can derive much more generic implementation for your program and the more generic your function is there is a less chance that that could be a mistake there the last structure that I'd like to cover today is travers it is quite interesting it we call function functor allow you to map a map a function f HP but Traverse allow you to map a function a to gb basically in this f is effective function so Traverse is a functor but it has much more additional capability if you have a traverse implementation like that then you can write almost any any loop and apply this kind of interval function on each element of the structure and note that there is an applicative there so applicative is there to do the effect full computation handle failures so this applicative machinery is there in Traverse to help you out on in the case of values and another interesting property is sequence so given that if you have a 4g of a you can get a G of f of a by applying this sequence function it's like a future no sequence I can give you an example for instance I have a list of option event and I can convert it to option of list of integer by calling the sequence function so using this kind of structure we can create much for generic function we can use those Combinator to derive our program quite automatically so the key takeaway from the top if there is any always use first class function the total function always use total function and describe your computation as data we have this misconception that ap functional programming doesn't have a way to handle if a full computation but on the contrary it has a framework for you given to handle effects and as you use total function as a side effect you will start using functional structure anyway so given your domain model if you use functional structure much of the stuff you can do drive automatically yeah by except yeah rather than writing a visitor's by hand you can get almost all those stuff freak yep so that that was a that was basically thanks for listening you