 I'm a self-taught developer and Ruby is the first language that I've used professionally. It's the first language where I've had the more obligation to produce code my teammates could understand. And it's the first language where I've had to understand code that somebody else wrote. But I also consider Ruby the first language I've learned. When I was younger, I toyed around with basic, and it was part of one of our classes at school. I played with JavaScript since I was in high school, and I wrote websites in the side throughout the 2000s in PHP. But I never truly learned these languages. I just kind of cargo-colded code I found elsewhere until the thing I was trying worked. So Ruby is really what I consider the first language that I've learned by committing myself to it. And because of that, I have, I think, a good understanding of how Ruby works and how object-oriented design works in general. I decided I want to learn other languages because I kept hearing and reading people talk about them. What they said seems a different form I was used to. Why is everybody talking about this thing called functional programming? How could Erlang possibly be more robust by letting things fail? How could Haskell be pure with no side effects and do anything useful? Why would anyone ever use Go or Rust or even C? Ruby and Python seem fine for that sort of stuff. People keep saying Ruby is slow. Is that really true? It doesn't feel slow. Every day on Reddit, Twitter, and Lopsers, there are hundreds of posts about languages and frameworks I've never used, and that's what got me interested to know what else was out there. So I installed some languages. I think the first one I played with was Closure. It had a lot of hype around at the time. I made a dice-rolling API that returned JSON arrays of random numbers. But toying with the language randomly didn't seem effective for me. It was very reminiscent of when I was installing Linux or BSD when I was younger. I would download an installation ISO, install it, and then that was about it. I never had a use for Linux at the time. I never actually tried to do anything useful with it. I never had a great, never had a goal of what I wanted to learn. I just compulsively installed it on a whim and then forgot about it. So I think you need a project. I think I need a project. This may seem obvious to some people, but it was a big deal for me. I needed a project or more abstractly, I needed a goal of what I was trying to learn while trying to accomplish. So I thought about what came to mind when I was thinking of programming. Most projects I work on are on the internet. They're either a website or an API for some client. And behind those websites and APIs that are other services being used over HTTP. So I think HTTP support is pretty important to me when I'm evaluating a language. But if I just have an HTTP endpoint that responds to data and I can't write to it, it's basically just a static website. So it would be nice if I could write some data to it. And if I'm writing data in the server, it has to work somewhere. One thing I don't want to do is use an external database because I'm going to try this in dozens of different languages. And I could use Postgres or Redis in a real app, but that seems like a lot of work when I'm exploring these languages. So let's try to keep the state of the world in memory somewhere. And that also forces me to think about concurrency. And last but not least, testing is very important to me. And that means TDD for me. I'm most comfortable writing code when I have tests that cover it, mostly because I'm lazy. And if I have to start an application and manually make a request to it to make sure it functions correctly, that sounds like a lot of work. So these are the things I wanted out of a project. So I wrote down different things that came to mind when I would satisfy these goals and I tried to pick one of them. And the project I came up with was a URL shortener. It's an HTTP API that will return a token for a URL. And then if you request that path, it will redirect you to that URL. And for simplicity, the token started at one, but if I was regarding a really little URL shortener, I might try to make the random or compress them with some kind of like base 62 encoding or something. So a client makes a post request with a URL in the form data to the shortener and it responds to one created with a slash one and that one is the token and also the path that the client can then use to get that URL later. When you expand and you get that slash one, that path from the shortener, it will redirect you to the website that the URL that was stored at that token. And if you make a request that doesn't exist yet, it should return 404 not found. So that was like my basic API that I wanted to implement in all these languages. So I wanna touch on a few code samples from this and a few languages starting with Ruby and the things I learned from them. To implement this on Ruby, here's a sample of code I came up with. Using Snotcher at a shortener URL, we grab the URL from the params and ask a shortener object to shorten it. This method returns a token and we tell the user what the token or path is with the 201 created. And then to use the token, the user makes a request for the token. We look up the token with the expand method which returns the full URL or nil. If we couldn't find that token be 404, otherwise we redirect the user to the full URL. Inside of the shortener object, we have two methods to shorten and expand. They kind of look like the Snotcher versions a little bit more in-depth. The shortener we generate a new token and store it in our hash of URLs and to expand we look up the token in the hash. And then there's a thing called an ID generator which is a numerator that yield sequential numbers and the URL's method is just a memoized hash. And to use the shortener class from Snotcher and actually have the test pass, we use a singleton pattern with a class method called instance that memoizes an instance of the shortener so that every time we ask for the instance we get the same one back. Is that code thread safe? If I run this on JRuby or Rubinius, will I get missing URLs or token collisions because two things are trying to update these objects at the same time? If I use Puma or Unicorn, should I have wrapped that code in a mutex? I have no idea. Let's talk about closure. Closure has these things called atoms. You create an atom and give it an initial state. So for this we start creating tokens at zero. Then you can use the swap function to send a function into the atom to update its value. So here we're using the ink function which increments the number. You can think of an atom as wrapping a value and it has a queue of operations to perform on that value and it guarantees that they happen one at a time. It's a little bit similar to the actor model. So if we call the next token function, it tells token to increment by one and return itself and we get one back and then two and so on. And I love how little code there is to guarantee safe and current access to the token. It's one function to wrap the value and the swap function is in place of whatever code you would normally write there to change the value. So it's really just about four to six characters and you count parentheses to guarantee safe concurrency to that object. Next thing I want to talk about is Haskell. Haskell is really intimidating when I first looked at the syntax. Here's our shortened method in Ruby again. The input to our function is a URL. We do some stuff in the background including incrementing the token and storing the URL and then we return the token. So Haskell is a statically ticked language and this is what a type signature looks like. These are optional. The compiler will infer most types for you but it's idiomatic to add them. Anywhere you see double colon you can say is a and anywhere you see a right arrow it's a function between two types. So you can read this as shortened is a function from a URL to a token. A URL and token are aliases for string but I find it helpful to be explicit about the meaning of these types. And this is what an implication of the shortened function or at least the input and output looks like. But if we try to copy the inputs and outputs of the Ruby implementation we kind of run to a wall. Haskell is a pure language which means it doesn't have side effects such as changing a variable in some other part of the code. And there are no objects to hold instance variables and no ID of self. So in order to shorten a URL in a pure manner we have to also pass in the state of the world and then the output, this could also be called state or shortener state or whatever. Then the output of our function if it updates the world will also have to include the new state of the world and then we can use another function to get the last token from that. So, and then the world is that ID, the int and then a map or a hash of URLs, of tokens to URLs. And then we can write our function that operates only in the data that was passed into it and only returns a new world and it doesn't affect anything outside of itself. So to get rid of that implementation and then add the type signature for the expand function where I learned about Haskell is without even seeing the code for the functions you can tell what they do. Not only can you tell but that the Haskell compiler guarantees it will do exactly what these types say. Let's talk about NIM, which usually called Nimrod. NIM is a language that compiles to C code and is also statically typed like Haskell. Here's some similar code in NIM but it uses global variables. But there's a difference between the return types of Haskell and NIM. Haskell returns a maybe URL which could be just a URL or nothing. But in NIM it just says it returns URL but what happens if that thing isn't there? So if you try this out, it turns out it says it returns URL but it could also be nil, which nil kind of sucks. By the way, every time I get a new method error in Ruby it's a type error. Let's look at go. A common convention people hate or fight and go is error checking. When you're using a function that could fail the function typically returns two values, the result in this case the number and an error. And then you check to see if the error is nil or present. Most people find this pretty ugly at first. But it's there for a reason. If we didn't check for the error and explicitly handle error cases we don't know for sure what the result would be. I love this tweet because it shows how much progress you can make when learning something new. In this case an experienced programmer, Brian Lyles, was but still new to go and now he's a keynote speaker, you have to learning it. But I also love that even Brian Lyles was fighting the conventions of go because I felt like the only one that was confused by these environment variables and how my directory should be structured. In Ruby we typically use nil to signify a failure state or quite literally the absence of the useful value but nil doesn't really carry any useful information other than it's not the thing you wanted. Sometimes we raise errors and rescue them but it's not really easy to tell if a method will return an error and which errors it might return. Let's talk about Elixir and Erlang. Elixir uses pattern matching to ensure that the return result matches what we expect. Here we expect to get an okay symbol back along with token. If things didn't go as planned maybe the shortener would return an error and message. The pattern match would fail learning to the problem. Instead we can use pattern matching to handle the happy path and four or four if anything else happens. The underscore just means anything else. Typically I don't do this in Ruby but it would work. It's not very idiomatic to see code like this. We also typically don't wrap things in a maybe or option or result class sometimes. Maybe that's because we don't have pattern matching in Ruby. But Matt has said that Ruby three could have type inference. It's not a stretch to think Ruby could someday be smart enough to know that this code doesn't work. I think people assume adding type checking to Ruby would be limiting but I wonder if adding type inference to Ruby would actually allow us to evolve and create new conventions and borrow more from other languages. This code I've written in all these languages is probably not idiomatic for each language but I've really tried hard to embrace the conventions of the language when I learned them. It's very easy to have a knee-jerk reaction when we see something we're not used to. Like why doesn't Go have version dependencies? Embracing the conventions of the language you're working in. Lots of smart people are probably doing that stuff for a reason. Vim helps a lot with this for me. There are so many plugins available for all these languages that Vim becomes the best IDE for all of them. Saving a file checks for syntax errors, does type checking, automatically imports packages you're using and sometimes even suggests better code. Another tool I've really grown to love is Make. Make runs commands and their dependencies. We typically use rake and Ruby to do this but when you're learning what the language is or returning to a language you haven't used in some time or sharing a service you wrote at work with a co-worker it's hard to remember the commands and their syntax and their arguments. Make files are executable documentation for what commands do what and also a rake file couldn't install rake for you or Ruby. So why do this? Why spend our valuable free time and our downtime at work learning these new languages? I think it helps to get a new perspective. I truly believe learning some of these languages has changed the way I think about programming. To progress our knowledge and challenge what we know it helps to move outside of our comfort zone. Language matters. I see a lot of parallels with spoken languages. Learning more languages allows you to communicate with more of the world and programming languages allow you to contribute to more code and projects. But also learning other spoken languages can change the way your brain thinks known as linguistic relativity and linguistic determinism. For me learning new programming languages has also changed the way I think not just in that language but in every other language I'm working in. I believe it made me a better Rubyist. I didn't have an explicit goal when I started to bring knowledge of other languages back to Ruby but it makes sense that learning more about programming makes me a better programmer. But I didn't expect functional languages to teach me anything about object-oriented programming or statically typed languages to teach me anything about dynamically typed language such as Ruby. And why would you want to do this at work? Why would companies care about this and why wouldn't companies want their employees to do this? If you have some variety in your technologies you use in your organization and you give people the power to move between or introduce new technologies you become much more attractive for people to work there and also to stay there. Michael Lopp of Rands and Repose has this popular article called Board People Quit and the gist is the most important factor of keeping employees happy after their basic needs of like money and safety is keeping them interested in work and fighting off boredom. So I have some random ideas for Ruby that I didn't touch on earlier in a new particular order. Testing becomes interesting when you're learning new languages. How do you test when you don't know what you're doing? I love RSpec but it's really complex for newcomers. This is a contrived example but subject let, describe and context and the stub and mock syntaxes, all of them are complex language on their own and over-drying specs can make them harder to read and stubbing and mocking requires a lot of understanding of how Ruby works. So I found that doing assertion style tests in an imperative coding style with local variables and even copy pasting code and not keeping it dry made it easier to write and comprehend when working in unfamiliar language. Another thing with testing, I always test outside in with Ruby and I use stubs and mocks heavily to isolate and object dependencies. When I'm writing Ruby, I'm very comfortable with this. I know I think how Ruby works and I understand what RSpec is doing. I understand what happens at the language level when I stub or mock something and how to set them up and what point mocks are verified. But when I'm writing tests in language, I installed 15 minutes ago. I don't know any of this. If I understand something, I can start building it with tests. There's very little that I understand when learning a new language, but I do know how functions work or objects and methods or whatever the language has, the small parts. So I think inside out is okay if you don't know what you're doing. So just start from whatever point you understand. So Scala has this tool called SBT. So it's kind of like rake. You do rake test or SBT test. But if you prepend a tilde to the command, it watches your files and reruns of the command without having to reload the JVM. So I thought it'd be cool what if rake had a similar syntax and by default it just rerun the command on any file changes. Maybe we could also control the pattern with like a guard-like regex in the task. Another thing I thought was maybe like in many languages, including all the JVM languages that use maven dependencies or install automatically when you run any command if they're missing. So why does Bundle give me this error? The chances that I'm going to type bundle install after I see this are about 100%. Bundle is the thing that installs dependencies so maybe it could bundle exec install the dependencies instead of yelling at me. Interview make objects with the new class method. But a lot of other languages use the class name and parans as a syntax for making objects or more likely records in a functional language. So why not Ruby? So I made this gem called Nuke and EWK to do this and it worked and it only broke like three things that were like really weird metaprogramming in this project that work. And I put it on Reddit, people didn't really like it. It's been said that you should learn new language every year. But a year is way too long to dip your toes in and see if you like something. And a year is way too short to dedicate yourself and really absorb the conventions and ideals of the community around the language. Start with a few hours a day and see if you like it. Then as you spend a week with it you'll produce something substantial and you'll need to address metaprogramms such as like how do I deploy or ship this and how do I upgrade dependencies. After a month you'll have experience interacting with the community and the honeymoon will be over. Pick a project. You could pick a URL shortener and you could do Conway's Game of Life which is a coding exercise that's very common at code retreats. If you've never been to a code retreat I highly recommend it. Or pick any other project that kind of resembles what you do day to day and what you like to do in programming. Maybe like graphics, maybe you like APIs. So which language should you learn first? I could stand here and tell you to decide for yourself which language to learn but I have some opinions. You should learn Haskell. You may never use it for project, I haven't but it's taught me the most about type systems and you should learn Clojure or any Lisp. It's taught me the most about how to think about my data and the syntax is incredibly simple once you get past the parentheses. And you should learn Go. It discuses conventions of modern languages to keep the compiler and language simple and this can seem like a step backwards but it's a great language for getting stuff done and I feel a lot of joy when I use it. Other than those I think two reasons I would pick a language are fun and community. Fun is how much I enjoy working with the language from a technical level. It's easy to create fun with dynamic typing but I find that these type systems help me think about the problem instead of the code and I find that fun and also how easy is it to read? What if somebody else wrote it? Can somebody understand my code and can I understand their code? And the community, is anybody using this and are they using it for what I'm using it for? How hard does it find help? And I think most importantly when I go to find help am I welcome or ridicule for not getting it? Is there like a vocal minority of people that makes you embarrassed to be part of that community? Or is it a very welcoming community, like the Ruby community? Some resources. I'll just hold it. There's two books called Seven Languages in Seven Weeks and Seven More Languages in Seven Weeks. These are great books that you kind of dip your toes in these languages in a way that's reminiscent of learning on your own. He goes into a repel and starts playing around with the language. Learn X and Y Minutes is a website that just has code examples for almost every language you can think of. It's great for learning the basic syntax of a language. Shameless self-promotion. I made this organization on GitHub called Kata Seeds. If you've ever done a code kata, one of the things that I find frustrating is trying to get the development environment set up so that your tests are running, and even in a language that you're familiar with, it might take you like five to minutes to do that. Or maybe you know enough of another language where you could just start doing the kata, but you don't actually understand how to write tests in that language yet. So Kata Seeds is a GitHub organization. It's a collection of repositories that already has a single passing test. So you can clone it, run make, and then you should be able to start playing with a language with TDD. And I really like this for a code retreats. I'm sure you're excited with somebody else. Have fun. Remember that sucking at something is the first step to being sort of good at something. And thank you.