RubyHack 2019 - A Rubyist's Survival Guide to Elixir and Phoenix by Benjamin Porter

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] all right now that I've lost all faith in Linux again no I'm just kidding I love it it never happens guys it works every time I swear but Murphy's Law so I brought the Chromebook which is also Linux so account alright so my name is Ben Porter gonna talk this morning about a lick sir in Phoenix kind of specifically for people that are familiar with Ruby can you guys hear me okay sweet so it's a little bit about me yeah I was gonna make a joke about how Matt stole the Ruby source code for me and I just whisked out so I'm not gonna make that joke but yeah if you want to look me up on key baiser or email or twitter i'm at freedom underscore Bend because somebody beat me to the freedom Bend so there's lack of coherence in there but yeah feel free to reach out because I love talking people all right let's jump right in so what is a lick sir so elixir was created by Jose Abele and for any Spanish speakers who think that I just English the name Jose a he's Brazilian so it's pronounced with a J so shall I throw that out there unless there's Brazilians who can correct me and tell me I'm wrong it's always a race anyway he was a rails core team member from early 2010 to mid 2014 and he received the ruby hero award so the reason why I mentioned that is that elixir was very much influenced by Ruby and you can definitely see that when you start looking at the language itself so I mentioned elixir was inspired by Ruby but it runs on the Erlang VM so if like me you have previous experience with VMs mostly Java and you have poor memories of wrangling the JVM the Erlang VM is a lot better it's a lot different not special on Java but and there's also a very good reason why the Erlang VM exists and it's the actor model so the actor model is out of scope for this and it's it's kind of a different way of approaching concurrency and it's definitely worth looking into I'm gonna get rid of this little notification down here there we go okay so elixir is more opinionated about how you write it which can be good and bad it definitely leads to more consistent code that in my opinion is much easier to reason about but obviously you lose a lot of flexibility so I tend to think of this as more of a good thing when I'm working with large teams and I'm reading a lot of code versus writing if I'm writing the code I prefer the flexibility and power of Ruby if I'm reading a lot of code sometimes I'm not super hip on them having the powers to do crazy meta programming if you've ever had a by bug prompt that starts and you look for the source and it says no source found in Ruby that's that's a good time right there so a lot of a lot of people refer to a lick sir as a new Ruby and Phoenix as a new rails I'm not really a huge fan of that expression although it's not I guess technically incorrect but so the question that would be why do we need a new Ruby oh and the new rails and I think I'd start by saying I don't necessarily think we do there's really nothing wrong with Ruby rails I mean obviously we're all here we use it Ruby is still under active development it's still getting great features same with rails they're still moving along so there's there's no reason you know like I've seen some people in the elixir community refer to Ruby and rails as essentially deprecated and that kind of bothers me because I don't I don't think in any way that it is it's still great platform but there are some things there's some trade-offs that you make when you adopt Ruby and rails one of them is scaling and I don't mean like the Twitter where they decided they got it they had to move to the JVM or thing like that but as long as you're writing your app well and you're following Kuroko's 12 factor app principles you can scale horizontally basically as far as you need to with Ruby it's not going to be a problem but when you start adding instances that gets expensive fast and so scaling with Ruby can be very expensive and now depending on what scale you reach the added productivity of your engineers may offset that cost but it may not as well depending on what scale you get to so that could be a huge benefit with Ruby when you're trying to to scale horizontally or at all really you also end up choosing between essentially multi-threading or multiple multi-process and for true horizontal scalability you have to be multi-process and for any high availability you do as well but both of those have downsides like multi-threading is notoriously difficult even people who do it all the time and have lots of experience they still occasionally will write deadlocks and end up with race conditions and things like that and also a lot of gems in the Ruby ecosystem are not thread safe and that's a hard lesson that sometimes you don't learn until you have weird things happening in production so it's definitely something that you should approach with caution so most of the time people go with multi-process but that also has downsides because you're duplicating a lot of memory and there's a little bit of overhead in each Ruby instance and so that is a bit of a downside and you also can't share things as well and then also there's a lot of really cool and interesting tech that's emerging around like WebSockets and alike and Ruby is definitely definitely gaining some ground there but it's you know it's it's a it's a thing that's emerged later so you know a lot of it is it's a it's a framework and a language ecosystem that's adapting to new technology which is great and that's definitely what we want but then also functional programming in general is also really gaining in popularity and in my opinion that's for some good reasons because it really can improve things for you so let's talk about some of the benefits of elixir elixir is is very much known for performance and when I say performance there's a bit of a misconception out there I'm not referring to raw CPU processing power necessarily it's the performance is is very much in low latency and things like that if you were to compare say elixir doing cpu-bound tasks compare that to rust or golang or see it's gonna lose by a decent amount but most the time we're not cpu-bound most of time we are i/o bound on either the network or the file system or something else so if you have a very CPU intensive task elixir is probably not the right way to go unless it's very parallelizable and then elixir really might be the way to go because a little bit of overhead but getting that actor/model that allows you to reason about your concurrency a lot better will be a very worthwhile trade-off so i elixir tends to be a lot easier to reason about because of things like forced immutability minimal side effects etc and we'll talk more about that in a bit but it's it's also a very clean language it's very lean and it's one of the first languages that i've ever seen where they essentially have said the next release is basically done like they're not going to be adding things to elixir at least not in the short term and you know some of the reasons they gave for that is that you know we see a lot of people have adopted golang because it is such a short and easy language you can read the go link tutorial and in four hours you've got a bulk of it in your head and so elixir is kind of taking the same thing but there's some amazing tech possibilities that elixir phoenix will essentially give you for free things like live view you guys haven't heard of that this was demoed last year at elixir comped by Chris McCord who's the guy that wrote Phoenix and he demoed 60 frames per second animation being rendered on the server rendered on the server to say that twice perfect it's amazing amazing the one of the implications of this is that it's still early days but there's plenty of proofs of concept out there you could potentially replace your client-side JavaScript framework with server-side rendering and elixir that starts to become a real possibility with increased performance because as we're getting more and more thin clients on the front end with the client a lot of times our server has way more processing power so you could actually get much better performance by rendering on the back end and then just presenting that pre-rendered on the front end so this is a gif which is actually a lower frame rate than the animation so it's not going to do it justice but that animation you see is being completely server-side rendered sent down to the front end through a WebSocket all using Phoenix live view and it's very easy to get started that's the kind of stuff that gets a lot of people very excited about elixir in Phoenix so let's talk a little bit about immutability I suggested that immutability could help make the code a lot easier to reason about I want to give a really quick demonstration of that so this is Ruby code this is just IRB and you can see we create an array cust ages and drop a few integers in there and then we call a dot reverse method on that array it behaves exactly as we'd expect returns an array that's reversed but then we wonder to ourselves did this mutate the original is the underlying array still the same thing that we defined so we check and sure enough it is it has not changed then we decide you know what we've got another customer here we need to push his age to the array so we call custom stop push and then we have the same thought did this change the underlying array it is not immediately obvious whether it did so or not so we checked the array and sure enough it has mutated our original this has been a risk in most languages for a long time in fact back in my java days which i don't like to talk about very much this sort of stuff made bugs that were just insane absolutely insane sometimes in enterprise Java you've got call stacks that are 70 frames deep and frame 45 mutates your your original structure and now you've got this insane bug that you can't find and so essentially what you end up doing is you defensive copy everything anything that comes into your method anything that you send you copy copy copy that is not sustainable or scalable it breaks down and so at a certain point you profile and you realize we have a ton of memory just sitting there from all these copies we got to reduce that then you reduce that and you introduce huge bugs and your life becomes horrible this is something that elixir makes impossible it is immutable every time you make a change to something you know to some data structure you are getting a copy back and it's not as inefficient as it sounds there's a copy-on-write mechanism but then there's also some things that they've done to alleviate that are out of scope so I'm not going to go into them but it's not as efficient as it sounds because that can be a scary and save anyway so functional programming we're essentially going to treat computation as the evaluation of functions much like mathematics so you know traditionally with programming languages they tend to be very imperative and we've got some better patterns that we slap on top of there but functional programming so much changes the paradigm and that we're looking at these more as mathematical expressions that do stuff so functional programming you're trying to avoid mutable data and changing state pete hunt really changed my outlook on state when he got up in front of world to talk about react and he said and I think this is this is a an apocryphal quote but he basically said state is the root of all evil and he talked about how so many bugs especially in a Kalina UI in a GUI so many bugs are the result of state and not properly understanding your state or not properly mutating it to the way that you need it to be and so he completely changed the paradigm with essentially always rerender and part of that was keep your state in a very focused place state is a necessary evil in functional programming especially languages like Haskell we try to pretend like you can be completely stateless but you really can't it's not useful as soon as you write to standard out you have mutated some sort of state and cause some side effects so it's really not the the pure model is is academically interesting but not pragmatic but we can make things a lot better by trying to stuff our state into a container or a package or a box so that way we know where it is and we can also behave as though our state is a series like with redux that I feel like I'm getting slightly off topic here but you you can look at state as very reproducible you you have you know clear a clear path clear steps to get to your state and that's something that functional programming really emphasizes a functional programming is also emphasizes declarative programming so in other words you're doing your programming with expressions or declarations rather than statements so it's kind of like you should tell your program what to do and not how to do it at a higher level at some point you're gonna have to say how to do it but especially when you're reading code it is so much easier to look at an algorithm and have a list of function calls that tell you what they're doing that's a very declarative approach and to the extreme you probably already use this all the time in fact Ruby one of the things I love about it is that it is a very declarative language it allows very high-level patterns like that but anytime you use a JSON or Y Amal config file you're doing this that's that's declarative and it's definitely a benefit functional programming also emphasizes item potency meaning that the output of a function depends only on the arguments that are passed to the function there's a related topic called referential transparency which I'm not a compiler writer but I've been told that the referential transparency guarantee of things like a lick sir can allow for powerful compiler optimizations because it only needs to invoke a function with certain arguments one time if it ever sees the same function call with the same arguments again it can just swap the value and put it right there in line it doesn't need to actually run the code again if it's a pure function or idempotent a functional programming also attempts to eliminate side-effects in other words changing state or changing the state do not depend on any on the function inputs there's no global state there's no global variables things like that you're not changing something outside of your function so let's do a brief comparison of object-oriented versus functional with olp a lot of times we think in terms of classes objects and instances of objects we tend to really focus on state you know with especially with Java they really kind of push the idea of encapsulation and so that's kind of crossed into lots of different object-oriented paradigms and so that can help the encapsulation is definitely improvement and that outside classes outside code should not have to care about internal implementation details only what's exposed but it's still only really a half solution because you still need to worry about that object being mutated by some other part of the code even if it's going through the encapsulated interface so functional attempts to excuse me so object-oriented is there's very much a focus on state and you call methods on objects which are essentially like behaviors with functional we change that paradigm we try to think more in terms of data transformation instead of in terms of objects now at first that's a really weird thing to think of but as I've gotten more into functional programming I feel like it actually is a more accurate model of the real world it's often times we really are taking data in some form and trying to transform it into another in fact if you think about web requests that's literally what they are you have some requests that's the starting data and your job as a server is to translate that request into some sort of data that you're returning their JSON or rendered HTML or something like that with functional again state is minimized and isolated as much as possible it's the root of all evil but it's a necessary one so we we try to keep that minimized as much as possible and isolated to the extent that that makes sense and then obviously the functional we're still calling functions but when we call functions we're not invoking them on objects they're not methods anymore we're passing all the data that that function needs as arguments including and we'll look at an example here in a minute but including like say if this function is jobs operate on a string you're not calling string dot sum method you're calling you know the module literal string dot sum function and then you pass the string as its first argument so functional I mentioned their data transforming is come the the approach you probably already do this in many ways like if you have any experience with bash or university shells then there's there's the pipe operator which is fantastic and that is basically the same idea so I've got this little snippet here of some bash code that essentially if you have a gitlab project and you want the merge request ID but you don't want to use the gitlab api because you don't need it then this would do that for you so we basically just use get regular tools enumerate the branch and stuff on the remote and then we pipe it through grep a couple times and through awk then to through said and at the end we have just a single request ID that comes out of that pretty cool but if you notice grep doesn't care about anything except its input it's only looking at the input stream and then it only cares about its output stream and then the next time you invoke grep it's not looking to see what did the previous grep do what sort of existing state is on the system it doesn't know or care and this is a beautiful thing because we can chain these things together we can reuse functions in different ways and what you might see it when you start programming a functional way is that your functions become useful in ways you never even expected or anticipated that they could be especially if you're writing if you're writing things algorithmically okay so as I mentioned you probably already do functional things and you may not realize it yet but things like map and sort and select and each and the way Ruby uses blocks those are all things that are very much functional in nature and with blocks you're essentially just passing lambda function I mean that's technically accurate but at a high level abstract you know waiting about you're essentially passing around sipping civ code that are functions and that's that's what functional programming does essentially so Ruby is a fantastic language to do functional programming it and so you can already start using functional techniques in your Ruby code in fact I would definitely encourage you to but one thing I will say is a Dave Thomas who wrote the ruby pickaxe book which probably a little bit outdated now but amazing book if you're if you're still interested in but he also wrote a book on a lick sir and he mentions in there that you didn't learn object-oriented programming overnight you're not gonna learn functional overnight so don't beat yourself up and don't feel like oh this is too hard you know if you're not getting it right away just keep going and you know taking in bite-sized chunks and it will improve your object-oriented code too so things that you can do to start integrating these techniques in your Ruby code try to avoid mutating data most Ruby functions are and this is another thing I love about Ruby most of them have the bang sign on the end if they're going to mutate the original so you can usually tell by looking at what it's going to do and I love that pattern there are a few exceptions that like the push that we looked at earlier but for the most part that follows so try to avoid mutating data when you don't need to and then also you know try to avoid side effects in global state if you're not using the return in your method that can be an indication that you're probably causing some sort of side effect and so that's definitely something to pay attention to if you're not using I mean or implicit returns so try to use modules more instead of classes and then in your modules try to focus on transforming data rather than objects so that doesn't mean like if you've got some user model for example that doesn't mean that you have to write you know some module that's you know something strictly you know I you can still write a module called user and then in that it has functions that operate on a user object but the difference would be that you pass that user in and it doesn't mutate it and it's not invoking methods on the user necessarily that's a bad example don't listen to me one word of caution though keep it Ruby don't go crazy and because your coworkers are going to hate you if you start using these crazy styles that they have no idea about it still should be idiomatic Ruby otherwise you're going to create yourself a mess so which is better right this is the ultimate in debates I'm actually going to say it depends there's probably die-hard functional guys out there that think I'm a traitor right now and that's it's understandable but I honestly believe that some applications are a lot better with a functional approach and some are better with the opie for example and I'm not I'm not a functional programming wizard by any stretch but when dealing with very big nested data structures like hashes and JSON and stuff like that I find the function approach should be a little bit difficult because especially when some you know some piece way up here depends on some piece way down here and you need to change them and stuff like that then it can get pretty hairy there's different ways to approach that but it's certainly not as straightforward as just using Ruby and mutating the data right on the spot and sometimes that mutation is way more clear what's doing but again try to keep it together try to make it obvious where the mutations happening okay so enough about the the abstract theory let's talk about some actual code here so the basic types an elixir are many of them are exactly the same as Ruby for the most part like integers it's the same I mean the the column on the left that's Ruby and elixir code it works in both so you can see with integers you can just declare them literals and base-10 just like you expect you can also declare them in hex or octal and also really cool which I would have loved back in day when I was working on a network stack is the ability to do a binary little literal which is very cool and then floats obviously you can use the you know point something something ii5 scientific notation and that works just fine and then ranges ranges are there in elixir just like ruby then we have atoms which are basically ruby symbols and there are minor implementation differences but for the most part you can think of them as the same and syntax is as you'd expect then we've got regular expression literals as well these are a pcre just like Ruby and the syntax is slightly different because we're actually using elixir sigils which I'm not going to get into but it's pretty darn close to Ruby so it's not it's really not difficult to doubt basically there's the squiggly are in front of your slashes and then it's Ruby so let's talk about strings because the vast majority of applications are not useful without some sort of strings particularly in the web so strings are a little bit different in Ruby but most of the time you can think of them the same way strings in Alixe are all utf-8 and they support the normal escape sequences that you would accept expect like you know backslash n backslash T etc but there's basically two different types and it elixir purist my cringe as I explain this but essentially you can think of them as single quoted strings and double quoted strings in Ruby they're basically the same except for interpolation but in a lick sir they're actually very very different and there's important reasons single quoted strings in elixir are actually character lists under the hood and lists in elixir are linked lists just like in Lisp so what that means is that all of your lists and in enum module functions will work you can iterate these things just like you would an array or a list and it will all work as expected but they are a lot less efficient to work with if you think about a linked list where every character is you know essentially one link that can get really unscalable really fast so most of the time you're gonna want to use double quoted strings under the hood these are Erlang binaries which is a fancy way of saying they're like a raisin see for example they're actual continuous memory bits and they're just Unicode bytes internally they're called binaries which can be a little bit confusing but that's really all they are they're just they're more like C arrays this makes them a lot more efficient to work with but the key is you have to use functions that are made to operate on binaries instead of lists otherwise they won't work but the good news is there's a string module which implements basically everything that you should need and so if you find yourself implementing something by yourself you know that that may happen but for the most part you won't need to but with with elixir just use double quoted unless you have a good reason to use a single quoted character list you just want to kind of default to the double quotes so let's look at some quick examples this uses the string module and in a lick sir we always invoke things as module dot function and then the pattern that they follow is the first argument is usually whatever would have been the object that you're dereferencing before so if you're if you used to would do you know some string dot at and then pass in index now you're going to pass that string as this first argument and there's a reason why we follow that pattern which will make more sense when we get to pipe the pipe operator in a minute but once you know I mean it's pretty straightforward to use this mostly looks like Ruby and you can see that it does what you expect now in this case we are I'm passing a string literal so it's not going to mutate it but even if this wasn't a literal I can know with assurance none of these methods and none of these functions are going to mutate my original which is an awesome guarantee to have so lists an elixir are basically arrays from Ruby so you can see here we created a list called ages slept with few integers in there and then we can call functions and pass that in so enum traversing about sort the enum module is useful for any sort of data structure that you can enumerate like maps and stuff like that but then there's also a list module for things that don't make sense necessarily in a math but make perfect sense at lists like the Popat and replace that things like that so pretty straightforward and also I must say the documentation for elixir is phenomenal it is really really good very approachable so it's very easy to find what you need so there is kind of a newer type called a tuple that's not really a thing in Ruby but don't worry about this too much it looks a little bit weird but they're basically just meant to be fixed size containers for multiple elements and they're really they're not meant to be enumerated at all they're they're basically sort of like the binary if you will but they're mostly used with pattern matching as a way to return multiple values because again we're not allowed to make side-effects so you're gonna find a lot of functions will return multiple values because there's no object to stuff those things into these are super efficient which is why they're used so widely but again if you're gonna need to enumerate this or if your nesting it pretty much at all do not use a tuple because you will hate your life so maps maps are basically the hashes from Ruby and it's essentially the same syntax as Ruby we're just slapping a percent sign in front of it and reason we do that is because if we didn't this would be a tuple which obviously is not what we want so you can see maps pretty much behave like you would expect you've got you know there's a there's a map module it's got house key and a keys and values and very very similar to Ruby blocks so blocks are used a lot in elixir even more so than in Ruby for example you can see in this we're defining a module called hello and a function called world and so you can see the the way you declare module is def module hello and then you can see there's a do after that so we're actually passing the entire modules definition as a block and then when we define the world function we're passing its code as a block again but you can see it it looks a lot like Ruby you know we're using the i/o module so we call iota put string which does exactly what you'd expect you can see parentheses are optional here as well they are heavily encouraged in a lick sir though so I tend to leave them off for things like put strings and stuff like that but for the most part you probably do want them a string interpolation works just fine just like you'd expect and then we can invoke this new function we just defined with hello world and then some string and you can see here I gave an example of how you can essentially write a script so where you've got your shebang at the top and it used to just say user been end Ruby now it just says elixir and this works just fine now there is some overhead because this house go through compilation and stuff so if you want it to execute immediately then you need to either pre compile it or use Ruby there is a simpler syntax for one-line blocks because it's functional language we emphasize short functions a lot of times our function is only a single line of definition so there's a simple syntax for in line blocks which is essentially slop a comma and then a do with a colon on the end and then your line of code and it works just as you'd expect so let's talk about pattern matching this is something that can be difficult for object-oriented grok and it certainly was for me it took me working through several examples and really thinking about it and then a certain point it kind of clicked and mostly I credit Dave Thomas for that because for writing an excellent book which I will recommend at the end of the slides but to understand pattern matching it's a really useful really helpful to examine what the equal sign means equal sign in elixir does not mean quite the same thing as it does in Ruby and C and Java and many of the other languages you probably used to but it often behaves the same but this the subtle difference in behavior is important so that's why we're gonna talk about in depth you can see here well so first the elixir in elixir the equal sign is not called the assignment anymore it's actually called an assertion in fact we often refer to the equal sign as the match operator that's pretty interesting change so let's look at the first line here age equals 23 this does exactly what you would expect it to do but that on the next line we do something in elixir that doesn't make any sense in most languages but it makes perfect sense in elixir we say 23 the literal constant 23 equal Age elixir cannot change that left side value or variable it is a constant you can't change the cons at 23 the entire world would burn down for math so but obviously elixir is fine with this it has no problem the reason why is because the lick sir just tries to make both sides equal just like you would expect from math so math when you were doing algebra you had a equation and there was an equal sign there and that basically meant both of these sides will evaluate to the same value that's the same thing that it means an elixir so the equal sign is actually more mathematical now an elixir than what you're used to so 23 equals age is no problem for algebra same snow problem with the lick sir but there is a slight difference so on the last line here we do 25 equals age but we already said age it was equal to 23 so this can't happen because the lick sir is not going to try to change the value of a right side it only tries to change the value of a left side so it looks and says oh the constant went e5 I cannot change that to meet or match age which is set to 23 so I'm gonna raise a match or so in elixir when you're first starting you're gonna see a lot of match errors this is what it's talking about your pattern match failed don't get too lost by the different terminology okay pattern matching also works on lists you probably already use this and maybe don't even realize it because this is how most destructuring works like in JavaScript for example it's same idea here in elixir you can define a pattern and a lick sir will try to make that match so the first line here we just create our ages array and then we use something totally weird we have two array literals that are identical and we set them we have the equal sign and that is perfectly fine an elixir again it's an assertion keep in mind that in mine it's an assertion not an assignment then on the next line we have an array of a B and C and we try to set that equal to our ages array and this works just fine and you notice the way elixir made this work is it set a to 18 B to 23 and C to 25 and everything worked just fine then we do it again with a totally different array and it works just fine you can see a B and C are updated now if we flip the arguments and we change their sides that would fail but I didn't put that in the slides okay maps so pattern matching works just fine in maps too in fact as I started getting used to pattern matching I got to a point where I realized pattern matching is by far my favorite way of grabbing values out of data structures at least simpler data structures you can imagine if this map was nested ten levels deep pattern matching would be an absolutely horrible way to do it but when it's a shallow map or a shallow list pattern matching is amazing way of getting values out okay if pattern matching was only for the equal sign it would be cool probably a little gimmicky but it's it's not pattern matching is also used for invoking functions and this is where it's real power comes in be structuring is great feature but the ability to invoke functions on patterns is what I think is like this is there's really two features of elixir that I love I can't choose the best but pattern matching and the pipe operator are definitely definitely they're also breaking news yesterday we might be getting pattern matching in Ruby which would be fantastic but anyway staying on topic here so I mentioned that functions can be both based on pattern matching so essentially what this means it's a lot like function overloading and say Java or C++ if you use that you essentially define a function with the same number of arguments in elixir we call that arity it's fancy words it just means number of arguments basically and if there's two functions with the same arity then elixir will look at each of them in order in order of declaration and find the first one that matches the pattern basically the first one where that equal sign would succeed if you don't get a match error that means it succeeds it will find the first one that matches and then it will invoke that one as I mentioned you kind of spent you specify a pattern in the argument list and we'll take a look at that here key to remember if no candidate matches an exception will be thrown so that is basically how you can see oh none of my functions matched so here's an example factorial is like the it's like the hello world of functional programming because it's naturally recursive problem which works very very elegantly in functional languages so no exception here I define a module called factorial and then there's two methods you notice they have the same arity which means same number of arguments and they're both called of the first one defines a function of takes an argument of the constant zero I would make no sense in a language like Ruby you would never do that it makes perfect sense it elixir because it only matches the constant zero so in other words we don't need any if statement in there there's no logic there's no branching it just magically works like you'd expect because if the if this function is vocht with zero it will return a constant one which is obviously our base case in our recursion so that means we want to terminate the recursion then the next line is what does most of the work it just takes in an n which matches everything and it takes that n and it tries to end times of n minus 1 obvious it's not super robust but if you pass a string what if you pass a negative one don't worry about that we'll kind of cover that actually the next line but not really not a great solution anyway then here at the bottom I just have this enum each and it prints out the stuff you see on the right I wrote it that way intentionally because it will be more familiar for rubyists but it's not really idiomatic elixir so we're gonna refactor that in the next few slides to be more idiomatic once we talk about the pipe operator so what happens if we flip the order of definitions of our of function so now the end which matches everything again is on the top and our of zero is on the bottom I'll give you a hint it infinitely recursive because there's no base case anymore because a literal zero will match an N and elixir takes the first one so it's going to take the first one every time it's going to try to n minus one that and that's gonna get you to a negative one and from there you're off to the races until you overflow the stack elixir will totally warn you at compile time it'll say hey just so you know couldn't match this one thing because there's a blanket pattern somewhere so this is probably not doing what you think it's going to do it is a warning you can proceed if you want I would not recommend it so let's talk about the guard cloth don't worry about the pipes on the bottom left I ended up reordering the slides and I was too lazy to fix the example at the bottom don't worry about we're gonna explain it so here's our factorial it still has the end at the top but we're using elixir guard causes now don't overuse these these are these are essentially ways of specifying more conditions that aren't easily expressible as patterns so for example we still have our of that takes one argument in n but then we slap on a couple of extra conditionals like when n is an integer and n is greater than 0 then this pattern will match again don't use guard clauses first try to do it with pattern matching but these guard clauses are there because elixir like Ruby tries to be beautiful and it succeeds and it tries to be elegant and if we didn't have guard clauses I think it would be a nasty soup so there you have it so this actually fixes our code and so now it runs like it did before it's just not super great looking so let's talk about the pipe operator as I mentioned this is also one of my favorite features and elixir probably tied with pattern matching this essentially lets us write things that are much more palatable way so I mentioned we're trying to build chains of transformation here we're looking at we've got some value and we want to transform it into something else that's kind of our base paradigm and approach with programming now and functional so let's look at what that implies without a pipe operator and I also say the pipe operator is really great for those of us coming from oo P as well because it lets us think still in a sort of low-key way which i think is really important because Opie is popular for a reason there's a lot of value in that approach basically all the pipe operator does is it takes the previous value basically whatever value is on the left side of it and it magically syntactically sugar Rises that into the first argument to the next function that's all it does so it's really not magical at all so let's rewrite that bottom part to be more idiomatic elixir so here's a here's a more idiomatic way now we're using enum join enum to map enum map we're broken it up into small useful functions and we're essentially blending those together now if elixir code looked like that you would probably not use it and neither would I that's hideous nobody wants to see that so how can we make that better well obviously the first thing we can do is add some white space so now you can see it a little bit more clear but it's still pretty gross like imagine if you had a pipe of that's you know ten functions long you're back to the node.js callback hell that tortured us all for a little while till we learned how to use promises properly and async gonna wait now which is even better so this is more idiomatic elixir like I said but it's still not great this is where the pipe operator will save us using the pipe operator we can break this down into a much more functional approach and it's it's it's now a very readable transformation chain so we start with a range 1.6 because we're trying to print out the first six factorial and then we just run a map on that we map it to a tuple of I so that we know what the original value was and we map that to factorial by so that tuple comes back and the pipe operator will magically make that be the first argument to our next enum map function and we pattern match on that tuple so we can extract the eye and the factorial because that's what we're interested in and we map that to a string and then that goes back pipe operator again passes out as the first argument to enum not join we join it on a backslash n so that we've got our nice formatting and then we that would be a single string value which we then use the pipe operator again to pipe to IO dot put string much much better approach okay let's look at some of the tools in Ruby and their equivalents in elixir because again there's a lot of similarities so if you if you love the Ruby ecosystem like I do then chances are you're also going to like the fact that elixir has a lot of equivalent so IRB the venerable interactive ruby has there's an IE X interact elixir which does exactly what you'd expect the IEX is a little bit more powerful it's really more like a combination of Prai and IRB but that's not a perfect analogy it's it doesn't you have to kind of try to get to some of the priors but anyway then rubygems the gem tool there's an equivalent called hex bundler is replaced by a tool called mix and same with rake so the bundler and rake is probably the more unique way to do it of have something small it does one thing it does it really well but mix takes the place of both of those and basically behaves like you'd hope so that's a whirlwind tour to elixir let's talk about Phoenix so Phoenix is a blazing fast highly scalable rails like framework for elixir you will see it really is very Rails like if you already know Rails you know quite a bit of Phoenix already and what you don't know is going to be pretty intuitive with a few exceptions the biggest thing for me after doing I built a little a little throwaway app in Phoenix just kind of get an idea about it and I noticed my response times that were being printed out were in microseconds instead of milliseconds that was indication number one holy cow performance performance is insane here and there's a few reasons why I will go into that but many times your response time will be under a millisecond so if they didn't use microseconds they'd be rounding to either 0 or 1 which is pretty mind-blowing there's also some proofs of concept out there that Chris McCourt is built for example that they have so many connections so many concurrent web sockets hitting this Phoenix server that you start to run into the you limit of Linux like your operating system stops scaling before your server does which is kind of awesome and terrifying same time so there's a simple example project I actually planned on writing one specifically for this talk I ran out of time and so I just grabbed a little micro service that I wrote a little while ago and so you're welcome to look at that if you want the code samples in here come from that but I'm not gonna really explain the app because it's kind of out of scope and I apologize for not having something better for you but yeah okay so I mean I said that Phoenix was super fast that deserves some backup and some proof so here's some benchmarks now let me first say I don't trust pretty much any benchmarks because they're so easy to gain and they're usually not trying to measure they're usually trying to measure the strength of whatever you're trying to prove is blazing fast for example so metrics takes them with a grain of salt and also these metrics are from Phoenix 0.30 we're up to version 1.4 now so they're kind of outdated but these were the best ones that I could find and I think they do a decent job at sort of illustrating the the thing here so you can see plug is at the top and Phoenix is actually built on top of plug and Phoenix itself adds a little bit of overhead as you would expect but not a ton and it actually adds more consistency just kind of interesting but then we've got Jen play an older version of Phoenix Express martini Sinatra and rails and you can see we're talking orders of magnitude faster than your your standard rails setup so that's a huge performance oh really oh man okay thanks we hurry this up so what makes Phoenix so scalable okay I mentioned that it's built on top of the Erlang VM which means you've got full access to the actor model long story short the Erlang VM was built by the telecoms to run on switches for phone companies so I had to be rock-solid and its benefited from decades of that the key to horizontal scaling is multi-process as we kind of talked about and you know licks are in Erlang that becomes pretty easy even for noobs Phoenix also fits really nice into existing architectures so here's another look at a Phoenix live view this one is a server-side rendered clock which is cool so let's talk about some of the major Phoenix components routers controllers views templates and then we have now something called channels and then ecto which is basically like active record but it's part of the most different part Phoenix router is built like a pipeline just like you'd expect from a language that emphasizes transformations so it uses the underlying plug library but the Phoenix router is very declarative just like in rails so here's an example this is this is the router and we've got two pipelines one's a browser one's an API if you're micro service or you app only uses an API or only offers an API or only browser vice versa then you don't need this but this one has a UI portion and then there's a curl script that hits the API endpoints so that's why there's both but you can see it's very easy to layout how things happen so in a browser we want some more middleware that we don't care about for API calls like cross-site request forgery protection and secure browser headers etc and then we've got a few endpoints you can see it's pretty much like the rails router with get resources post just like you can do rake routes and see your routes there's an equivalent for Phoenix PHX routes so controllers basically do what you'd expect it's part of the request pipeline gets invoked by the router and the difference is you take in a connection and I use the word object lightly it's basically a map or a struct kind of thing but you take in a connection map and then you essentially add things to it so you transform it by adding things like your response codes and stuff to it and then Phoenix takes care of converting that into HTTP usually you're making some sort of database query you do some logic or render of you so here's a slightly more you know slightly bigger controller but obviously not big this basically uses there's a little piece of middleware that pulls the users information on the database when they first make the request and so if it's there then I redirect them to the page they really care about if not I just send them back to login this is a session controller so basically it's how you log in you create a new session just a few actions here a new create delete pretty straightforward but what you might notice right away is there's a lot of pattern matching in here and that is by design essentially we we really reduce and eliminate the need for if statements and with pattern matching which can be really nice this is slightly more complex this is a user controller so you can see it's got a few more actions but for the most part it basically just sort of declares what you want it to do render some template or you know render some view and you know grab some data from the database that sort of thing so a view is basically a module that just contains rendering functions the key takeaway is that it's the views job to return to render something if it's just JSON that's why as HTML that's good too sometimes it'll be a little bit more but there is a difference between views and templates templates are basically embedded elixir or eex which is basically the same thing as ER be these are super fast and phoenixes one of the ways there's all the ways they get response time super low is internally they're linked lists instead of strings which helps a lot okay so there's a template you've seen those many times channels super cool thing about Phoenix they're basically stateful conversation so the old request/response thing where HTTP being a stateless protocol we have cookies and stuff in there we don't need that anymore so we actually can oftentimes do away with cookies and maybe even the database you're keeping session information to Redis for example you may not even need it anymore these can be implement with either WebSockets or long polling or you can build your own protocol which is really cool but ecto this is actually the last last section here so an ORM for lick sir we're not going to go into it I wasn't planning to anyway but it basically uses macros which are like Lisp macros and so the DSL is really cool it's a lot closer to sequel than active record for example you see migrations here they pretty much look like they do in rails for the most part here's one that's actually related you can see we've got our has one and it belongs to just like you would expect from rails so pretty straightforward and then the this bottom one is an actual query so you can see there's a from function that's defined then we pass in some code which the DSL will translate into sequel but it's a lot closer to sequel and it also only responds or returns data you explicitly request so no more accidental dot stars and things like that deployment is very simple with the lick sir just like it is with rails Heroku is your gonna be your best experience in my opinion there's build pack for Phoenix and there were a little bit of bugs earlier on but they've they've made rapid improvements and it was a third-party thing it still might be but Heroku the guys from Heroku actually get on the github and send pull requests and give advice so they're definitely helping out which is freaking awesome it's exactly what you'd hope from the platform the service giggle XAR is also one it's it actually supports a little bit more than Heroku but it's a little bit more expensive and then if all you want to do is just slap a docker container you can do that easily because cowboy is like the server that comes with it and it's production-ready or you can do go all-out which we're not going to cover we look to replace Ruby no I don't think so Ruby's always gonna have a place it is by far the best scripting language and it's also great for prototyping and most applications are constrained by Big O mistakes not by performance so happiness probably matters more developer happiness is a huge constraint that's basically it there's some advice for further things if you want to read more and these are two excellent books that you might want to check out the Dave Thomas we're going to left Chris McCord one on the right it's actually an early release but it should be out soon and here's some more stuff and if you want to contact me there's my information [Applause] [Music]
Info
Channel: Confreaks
Views: 9,118
Rating: 5 out of 5
Keywords:
Id: sSoz7q37KGE
Channel Id: undefined
Length: 51min 39sec (3099 seconds)
Published: Thu Apr 11 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.