Ruby versus the Titans of FP

0 0

all right good morning everybody that was born Susie I thought I thought everybody but me went out to drink last night all right and welcome to my keynote Ruby vs the Titans about FFP I'm gonna talk a little bit about myself so that you know where I'm coming from with this because it's gonna be more of a story so I've been a hobbyist programmer for about a decade the very first thing that I ever programmed I was 12 years old who here remembers QBasic on like ms-dos who here remembers Gorillaz my very first line of code was to cheat in that game I made my brother always play player 2 and I made his wind values display something other than what they are just a little bit - left or right so that he could never hit me and that began a lifelong love of programming I'm also fascinated with functional programming it's a very mathematical discipline and it's it's very easy for me to reason about what I'm doing however I'm relatively new to industry I'm at my first job at Mavenlink a little company you've probably heard of from the three other speakers I've been wandering around here and I've been there for eight months and it's my first real experience with Ruby and so while I fix experience Haskell and closure and JavaScript it's only really this year that I got in a Ruby yet somehow I snuck on to the stage of Ruby conf as a keynote speaker I'm still trying to figure that one out all right so when I first started learning Ruby my motivation was like oh hey like I need to do this for my job but I also wanted to drag all my functional knowledge with me and not many people use Ruby in a functional matter if you want to create something you you have an object for it and it has methods and everything's oo and it was like okay I'll roll with it until a few months ago when somebody poked me to do a CFP and I finally decided to actually explore it and I was like okay I'm gonna make functional programming work in Ruby and this talk is is a documentation of my past three months distilled so that you don't have to see me yelling at a rebel trying to figure out what's broken alright so welcome to Ruby versus the Titans of FP before we get started we want to talk about what functional programming is because not everybody is familiar with it now if you have 20 people don't give you 20 different definitions some old people with math several people some people will bring out their Bible of category theory and sit you down for a three-hour lecture I like to be a little bit more pragmatic so to me I like to describe it as this it's a programming paradigm that focuses on functions as transformations over generic objects and data structures as opposed to objects that we model a good example of this is a login object now if somebody I think everybody here is right now login the object if they've worked on some web app and usually when you approach it you're like okay I'm going to give it some attributes and it's gonna know its internal state but then it's also gonna know how to validate itself it's also gonna know how to delete itself so we're gonna have to save itself to the database functional programming separates those two things so you can still have a login object which can end up being a generic data structure just like okay I know the user I'm associated with I know if I've been deleted but then you'll have a separate set of functions that can operate on that object for example to invalidate it so why is that a good thing well one is easier to reuse logic and I think this is one of the biggest benefits for it I'm pretty sure everybody's ran into the situation where they're like oh hey this class has an amazing method that I really want to grab and but I can't subclass it for whatever reason because inheritance just scares me sometimes and so it's really nice to have that already separated so that you can then figure out how to make a generic for an entire class of objects lets you compose smaller units of logic together which will be an entire ten minutes of this presentation and it can keyword can and giant air quotes result cleaner and more performant code if it's done the right way if it's on the wrong way it can get kind of scary right so these are my five key features of a functional programming language and this is probably contentious if you go to like the functional programming lexicon there's like 300 definitions inside of it but these are the five things that are important to me high order functions carrying composition functional purity and immutability we are only going to talk about three of those today functional purity and immutability are things that we can do through their data structures that we use and through how we treat the flow of our program however higher-order functions composition and currying tend to be things that are more primitive in our languages and if we don't have support for them we end up losing a lot of things so today I'm going to compare Ruby verses closure Haskell and JavaScript in order to see if we have those things that make other functional programming languages nice and we're going to start with a little thing called closure now closure was born in 2007 and it is of the list family and that makes some people very happy that makes some people very sad so what makes it a functional language it has higher-order functions it has first-class functions which this means that we can take a function and stuffing into a variable and use it anywhere we want and then it's standard library has immutable data structures and this makes me so happy and I wish I had more chances to use it this is an example of what a function call enclosure looks like it's just a list the first thing that in the list is a function everything else its arguments Lisp is actually short for list processing and this seems elegant and simple and nice but then you end up with programs that look like this this is a lazy take and those parens at the very bottom of the slide either make you make a face like that cat are you put on a robe and you become one of those old wizards of lists right but we're not gonna dive into anything crazy with closure we're going to talk specifically about map going back to our definition map is a function that takes arguments what are those arguments well very first thing is a function that we want to transform some collection the examples that we're going to use on that you're going to be sick of it at the end of 35 minutes is going to be increment where you're going to increment and decrement so many numbers it's going to be great and then the second thing that we pass in is a collection which is going to be a standard array for our purpose and so this is what it ends up looking like we we map our increment function over 1 through 5 it returns 2 through 6 and we've already arrived at the first capability which is higher-order functions a higher-order function is a function that either takes in other functions as part of its arguments or returns a function as its result the example being our map taking in a function for its transformer can we use this in Ruby obviously I'm pretty sure everybody just like everybody knows we can do this so the standard way of doing a map in ruby is to have some collection and call map on it as method and then pass an argument we're going to create something a little bit more generic for reasons that will slowly become clearer as we walk through this so we're going to start with a proc and give it our two arguments function as our first argument which makes this map a higher-order function and then our collection and then we're going to cheat and we're going to use we're just going to call the collections map method if it quacks like a duck it must work for us and then this is if you've ever read the closure documentation F and Cole are used all over the place and this is what they actually stand for a function collection congratulations you can now read 90% of the closure documentation because that's all it is it's all it's all signatures right so how do we use this well here we're going to create an increment that just takes a number and returns one to us we are going to give ourselves a list of numbers two through four and then we are going to use map call in order to invoke it with our arguments this drives me insane fortunately ruby is nice to us and we can hide it this was a mind blow like two months ago when I was like learning and I was like okay I'm gonna get tired of using call anywhere oh there's syntactic sugar for it there's good and this is at the interpreter level so if you define an object that has a call method on it it has this alias on it as well and so you can hide all your calls it looks kind of beautiful the dot still throw me off because I want them to look like method calls but it works out a more complex example going back to the logins that I keep talking about is like okay we're going to create an anonymous proc Det not really anonymous we're stuffing into something we're gonna create a proc that takes a login and sets the deleted at the time now and we're going to say okay is delete it at not nil cool we deleted something and then we're going to create a list of logins or a set of logins or a vector of logins I have not found a use for vector in an actual production code yet and we're going to map over that with our and validate login helper and so we've met our first requirement here that we can use higher order functions and the syntax is a little bit different than what I'm used to but it's there and so the next thing we're going to cover is currying and we're going to turn to our old friend slash enemy Haskell Haskell is from was created in 1990 so it is one year younger than me and I officially feel old now and it's from a family of languages called ml which is short for meta language which I always thought was machine language until like two days ago and somebody corrected me on this and the things that make it a functional language is that everything is curried inside everything is pure and it has the most beautiful type system that I have ever seen in the language although it also drives some people and saying so what does a function call look like in Haskell well here we're going to create our increment function by calling add which is just gonna be a plus sign here with 1 and then we're going to take that increment and then pass it in to map with 2 3 4 and we get 3 4 5 as we expect but what was with that Inc add takes two numbers and we only gave it one and it didn't blow up and give us our argument error this is strange that's investigating in the worst possible way so hit so we're gonna talk a little bit and talk about hindley-milner type systems um which might be a little bit heavy-handed but will help us figure out what's going on here so we can be here talking about them forever but two things that we need to know are are there is that it can be used to express the signature of a function and that Haskell uses it to annotate types and it will check against the types that you annotate an example add or at least add as we're used to we have a function name here and then we take two arguments here and we just group them in parens to say that we're taking them at the same time we have a function arrow which tells us were going from something to something and it gives us a result congratulations you all can now read types of interest in Haskell for the most part alright so this is how we're used to doing it we call add with two numbers it gives us one number it yells at us if we give it yes this week we give it one number and blows up and we get all sorts of weird things and then we get a thirty mile long stack trace and it's never fine Haskell does things a different way so Haskell still has an odd function and it still has the same behavior where it takes in two numbers and it gives us back a number but we can only give it one argument at a time and when we give it an argument it will return too a function waiting for the rest of us arguments and so let's go back to our Inc and figure out what happened so we have ad here and we give it one and so we pass one in here and you can guess what we got back a function waiting for the second number and this gets us to our definition for a curried function and this is why you do not rewrite slides at 10 o'clock at night before you present this slide right here I rewrote this three four times hated definition and I just said okay it's midnight I messed with this anymore so a courage function is a function that upon being applied to less than the total set of arguments that supposed to takes returns another function waiting for the rest of his arguments so our add function takes two things we give it one it returns a function waiting for one if we had a reduced that took three things and we gave it two things it would return as a function of waiting for the last thing so and so forth if we had ten and we gave it five it gave it would wait for five more and just give us a function waiting for that and let's just do some borderline dangerous things so we're going to implement this in Ruby and we're going to use our best friend closures in order to do it so here we're going to create an ADD and we're gonna have a proc that takes an X and when we call this with a number it's gonna return to us a proc that waits for y when we call this then it will give us results so we're gonna go create our friendly increment function by just saying add one and it's going to bind that one to X and we're going to get a function that waits for Y and we'll just add one to whatever Y is so we can call immediately and we get Inc 2 and we expect 3 or we can map it and we get our incremented array as we've done the entire time I hope hey oh this is the easy slide so there is a better way nobody wants to write 20 nested closures luckily we have native kerning this made me jealous because I came from JavaScript and we don't have native Korean JavaScript so when I saw this I was really happy cuz then we can end up writing our functions like we normally do X Y Z do a thing have it automatically carrying handle for us and in addition to doing all the things that we can do occurring we can also just call it at any time with all of its arguments and then we just get the result back immediately so a quick the versions in the order of arguments you'll notice that Haskell closure and our Ruby map all had the same order of arguments and this is why if we wanted to create a function called Inc map that increment that iterates over a list of numbers and increments all of them we can build it this way assume that our add in map or curry what if we wanted to decrement everything instead well that's easy enough to do we use our add and just give it a negative 1 instead of 1 and build up the function in the exact same way and through carrying we can build entire families of functions of a primitive I I could write a mathematical proof right now but it would not be good I can create every step function from my add I can create own every iterative transform function from my map and that makes it easier for us because then we don't have to worry about those things and we get to abstract a layer away so we've got higher-order functions and we've got kerning what's left our best friend JavaScript so javascript is relatively new in 1995 and it doesn't really get to be put in a family it's kind of like scheme with C syntax and so it's not really like anything else and so we get to name it its own thing what makes it a functional language well first-class functions which we have determined lets us do higher-order functions and closures which let us do kurung but past that doesn't really have much it has really strong functional programming libraries but not many things built into a language and so despite the fact that we have all of these really low-level tools in JavaScript the core API doesn't really like us doing things and FPS on some places example array so we created Rea three numbers map is a method on array and so if we want to access map that's how we have to get it pop gets a little get something off of the end of the array and gives it back to us and it mutates the array underneath which destroys mutability and doesn't really work out for us in a functional flow wait a second don't know what Ruby do these same things it does exactly the same things actually map is also a method and pop also mutates and so a lot of the stuff in the standard library that would create friction for a functional programming flow exists in both languages so that gives us a clue because we can look at how functional programmers in JavaScript solve their problems in order to figure out how in Ruby I can solve my problems and the answer is functional programming libraries in the beginning there was underscore it was okay kind of but it got the order of arguments wrong and solo - came around but unless you lose low - Effy nothing was career so Randa came by and everything's curried and everything's in the right order of arguments and everything has the hassle names for his functions and it lets you pretend that you're not in a browser and so it ends up being my favorite thing out of all the things in Rambo my favorite thing is composed because this lets me glue things together how many people have ever done this it's you write a method it takes in the thing you invoke it and you save off the result to something and then you take that result and you immediately pass into another function and then you say well that result and then you go Pass save pass safe pass save pass they even told 30 years later you're finally able to return the thing from all these functions that you've been running there is a better way in this case Rambo gives us a nice compose Rama also gives us all these nice courage functions and so I'm going to define add to here as oh hey I'm gonna create an incrementer we'll add one glue those two functions together and then the argument died passing gets passed into one of their incrementer x' the output of that gets passed into the next thing for matter and so we create a chain or a pipe of functions here that's our add to two returns for this is a little bit more complex and if you if the type signatures make you feel weird ignore them they're not there they're slightly greyed out for everything here we're going to create something a bit more complex mapreduces so if you wanted to do a MapReduce usually you have some function that takes in something call this map saves officer result calls reduced and then passes that back here Map Reduce are curried because they're in RAM the standard library map takes an increment well a transformer and a collection we give it a transformer it relieves us a function that waits for a collection reduced takes in a reducer an initial value and a collection and it returns a final value and because the types match up for all of that stuff we get to just glues them together like this this also lets us be point free so that we never have to mention our data and if we never mention our data I can't miss Titan which is why I love it so benefits we get actual easy logic reuse if we create an increment function and a decrement function we can glue them exactly two ways together but if we had 20 things that we could put those together like Legos any way that we want and so we get actual reuse of logic and we can do very strange things with it we can also compose our compositions and this makes for really easy refractors a really good example is a response for request response cycle I have a composition that takes a request into some internal state I have a composition that does something with their internal state and then I have a composition that takes the internal state and maps it to the response object I can isolate those trees separate things and then compose them together and make my entire request response cycle into a single function which ends up being really clean when you have 300 routes so how are we going to do this in Ruby like all things here we're going to create a binary compose that just worries about two functions we're going to create a outer proc that takes in two functions and it's going to return a proc that waits for the arguments once we get two arguments we're just gonna call Y with the arguments and output of that is immediately going to be passed into X the reason that this goes why the X instead of XY is because it works like mathematical composition there is there's usually a pipe function does the opposite order in most functional libraries and so we're going to create our ink here and we're going to compose it just like we did in a JavaScript example and we if we add two to four week it's expect in the same world if we wanted to do this to more than two functions then we can take the compose that we just made made and create a very outer compose and here we're just gonna say oh hey we're gonna take in all of our functions and then we are going to reduce over that with our binary composed operator and that will do the exact same thing only with as many functions as you like so here we can create an add 3 from 3 increments why we wouldn't just use add 3 to create our add 3 it works out for this and then if we add 3 to 4 we get seven back nobody wants to write this this is one of those snippets of code that you'll find yourself using everywhere once you're used to it and so the next question is what if I just want a gem I wrote it and so if you go to ruby gems and you look up reductio it only has three functions add compose and pass because the river three functions i need for this demonstration and it works the ad is curried as you can see and the compose is very attic so you can use it to glue 20 functions together is that a good idea never can you do it yes I have it's not pretty don't don't ever look at my github it's a scary place alright and so this is is something I'm very happy about because I got comfortable enough in Ruby to create a functional library and so what have we establish we can do well we set out to find higher-order functions composition and carrying comparing it to three different languages and we found all those things we haven't looked at functional purity we haven't looked at immutability put the tools that we have are enough and so while people like to pit Ruby against other functional programming languages nothing stops it from being part of the circle - and that's what I've got for you today oh I went through that way faster than I expected which means I have question time interesting so next steps if you want to learn more about functional programming dr. frisbees mostly adequate guide to functional programming is a book on github and is written by a cute badger that is actually Brian Waldorf who is a functional program guru at Netflix all the examples are in JavaScript but it's easy enough we also have my repo for reductio which is also a gem now I want to get more discussion going because I feel like I've trapped myself in this bubble trying to divine things the hard way in order to try and learn things but now I want to talk with people about it so if you're on Twitter tweet questions at me or just ask me them in the next ten minutes and then tweet with the hashtag function Ruby and I'll be looking at and I hope other people will be looking at so we can we can start a discussion and then please help me I have never written an open-source gem before and your heart is confusing Tara has convinced me that I need documentation so if you're a functional programmer and you would like to help with that or if you're not a functional programmer and you want to learn please please go help this and before I'm done that's my contact information on the left I want to give a shout out to um trans cord first I want to give a shout out to Ruby conf because it has been a hard week for me as a trans woman of color and I have felt very welcome here and I am very glad it's my first Ruby cough too so I'm happy that I made it out here but if you know any trans people or Alex I looking for a sense of community the link up top is a link to transport which is a global support group that would love to have more members and if people have not already sold you on to kool-aid that is maybe linked yet then we have this nice engineering blog link here that covers our a bit of our culture and so I think we are good for questions I was not prepared for this yes okay so the question was that they have trouble seeing the benefits of point freestyle and it's it's it kind of seems obtuse and not indirect because you never mention your data so you're never sure where your data really is and so one of the things that really appeals to me about point freestyle is that we get to walk away from the constraints of our data with the MapReduce that I demonstrate earlier it doesn't matter if it's an array or if it's a linked list or if it's a maybe or either or an aisle or some other some other func door I don't worry about that I just know that there is an interface that anything coming into this function has to deal with and then that lets me use it generically and so that ends up being that ends up being part of the benefits for me because I can just say in my type annotations like oh this takes in a monad and it returns blah and then I don't really have to worry about my data test that does that answer your question yeah so it's kind of like dog typing yeah so in in in JavaScript it's actually treated that way so if you use Ram de and you pass in something that has a map method on it as the mapable to map it will use that method on it and so you get something akin to that okay so the question was I'm please correct me if I just completely fudge this is that eventually you have I Oh eventually there has to be some side effect all programs have side effect otherwise what are they really doing right and so how do you handle that in functional programming how do you deal with that when you have basically two Styles where you have an object definer 200 style that has to do these things but you also want to create this logic and a functional style and if you went to the composition talk James actually had a really good thing about this so go back and watch his talk but I'm going to try and approach this well my first instinct is just tell you use a IO monad but that doesn't exist in Ruby yet I'm working on it so that's that's one of the things we're right now in our company I'm on a team that's using it react and so the way that I handled it is that when I do compositions I push that to the very end of our chains and so you can because composes cut because compositions are functions themselves and you can compose them with other things I can create a pure composition and that goes from point A to B and then in my actual app just compose that with something that takes in data and causes a side effect and so you get a you can you get to create a very clear divide there and say like oh hey this is my pure code inside of this library everything is beautiful and then the side effects get pushed to the edge of your app and that's how I tend to handle that I have strong opinions about active record scopes okay so so I I wasn't able to hear the second part after scopes okay um the question was how do I feel about active record scopes ignoring the rest of the library um I like that scopes are just kind of composing on each other in order to build a se query I don't like that at any point somebody can cause a side effect by doing something that actually looks from database database calls or one of those things where it's like oh hey I knew I loaded up a user and I automatically went to the database and I'm like yeah can I separate that stuff somehow and so when I write scopes I try to write them to say well I would write composition is where I try and push anything that would possibly load as far down the chain as possible instead of being like oh hey like I need to load the association for this thing and then a database hit occurs because of that it's the nice your question I feel like I'm back in class I should be saying every time - pudding all right anybody else who I have not okay so the question was have I done benchmarking because they have heard that currying might prefer mic what are words words are hard it might impose a performance penalty and so I have not done that benchmarking I want to do that benchmarking when I start fleshing out reductio gesture that we can say oh hey like this is a performance penalty you can do three less operations a second but it gets you such beauty and so that it has a thing that I also want to work on I have not heard of it but I've also only been a Rubeus for eight months so my opinion might not be always qualified all right that answer your question okay that's good so let's see if I can go back to so the question was why do we not want to compose twenty things together why do we want to keep our compositions short well and this is act this is actually really good demonstrator so looking at the types increase for this this is already kind of complex this is doing a lot of things it's iterating over a list is applying a function to everything to that list we get a new copy of the list back with all of those things transform and then we start folding over that list in order to get a final value this encompasses a lot and if we were to start adding new things on there like we want to map and do X saying we want to map and do y think we want to map and do C thing then it's really easy to just look at a page of a composition and be like what does this even do I do this all the time and that ends up being a problem because I'll go back to old code for example something I did for hackathon I was like oh hey I could bang this out in five minutes because it's just one giant composition but then when I go back and work it later it's like what was I doing it helps that there is a vocabulary when you use a standard functional libraries you'd be like okay this thing is head so I grabbed the first thing off with this array and then this incremented it and then dissipated it to something else but it still it still gets to be difficult once you get past a certain length and because compositions can be themselves made of compositions it's very easy to say okay I have 20 things here let me grab these five put them in their own composition and then replace those five at one line referencing that composition so it also gives you easy refactoring which ends up being a beautiful thing cuz then you can have your short like three five line things but then still encapsulate all of your functionality into one single function that gets passed and has a callback to something that is your question cool okay about four minutes left anybody else no last call cool thank you