Learn by Linting: Using Linters to Help Learn Programming Languages

Article summary

I recently started using Exercism to enhance my proficiency with Ruby. It’s an excellent tool for learning a new language or refining your knowledge in a language you already know. The best part about it is that you can submit your solutions for a mentor to review and provide feedback.

After I had received a few helpful critiques on my code style, I decided to find a tool that could help catch minor formatting mistakes before I submitted my solutions for review. The first tool I tried was RuboCop, one of the popular linters>) available for the Ruby programming language.

Linters can do a lot more than simply warn you about formatting mistakes (i.e. indentation, trailing white space, etc.). Linters such as ESLint and TSLint are plugin-based, which means you can add different sets of rules based on your current project. For example, in one of my previous posts I wrote about how I used eslint-plugin-ember to enhance the Ember application I was working on.

Linters can be extremely helpful when enhancing a large project where multiple developers are working simultaneously. This is certainly the case for projects using dynamically-typed languages such as Ruby or Python. Linters enforce a uniform coding style throughout the project, which makes “search/replace” refactorings easier and reduces the amount of code review comments that are focused on formatting.

At first, I thought that RuboCop would get in my way, warning me about trivial formatting issues while I was trying to complete an exercise. Overall, though, I found it to be more helpful than harmful. Let’s walk through a quick example to show you what I mean.

Learning By Example

Imagine you were tasked with writing a method that takes someone’s age and generates a response based on it, but you also want to yell your response back at the person. If you’re new to Ruby and come from a Java background, you might write a method like this:

# example.rb

def determine_response(age)
  response = ''

  if age < 10
    response = "You're less than 10 years old!"
  elsif age < 21
    response = "You're less than 21 years old!"
  elsif age < 40 response = "You're less than 40 years old!" else response = "You're old..." end response.upcase end puts determine_response(25) #=> YOU'RE LESS THAN 40 YEARS OLD!

Let’s walk through this example. First, we declare a variable called response and initialize it with an empty String. We then conditionally modify the response variable with an if statement based on the value of the age parameter. With the response variable modified to match the appropriate response, we return the uppercase version of it by calling the String#upcase method to make sure we are yelling our response. We don’t need an explicit return keyword since Ruby always returns the result of the last statement.

This seems like a reasonable solution. Surely there are better ways to write this method, but for someone who is just starting to learn Ruby, this would suffice. With that being said, there is at least one thing we could do to improve it. Running RuboCop against the above code provides this warning:

example.rb:6:3: C: Style/ConditionalAssignment: Use the return of the conditional for variable assignment and comparison.

Our code performs its task as expected, but RuboCop is trying to warn us that there’s a more idiomatic Ruby way to reach our goal. What does it mean by the return value of the conditional? Remember, Ruby will always return the result of the last statement. This means that we don’t need to modify the value of our response variable in every branch of the if statement. Instead of initializing the response variable with an empty String, we can initialize it with the return value of the if statement, like so:

# example.rb

def determine_response(age)
  response =
    if age < 10
      "You're less than 10 years old!"
    elsif age < 21
      "You're less than 21 years old!"
    elsif age < 40 "You're less than 40 years old!" else "You're old..." end response.upcase end puts determine_response(25) #=> YOU'RE LESS THAN 40 YEARS OLD!

This isn’t a typical feature that is available in other languages, and if I weren’t using RuboCop, I probably wouldn’t have found out about it for quite some time.

When I set out to refine some of my Ruby skills, I didn’t want to write Java programs with Ruby syntax. I wanted to learn how to write idiomatic Ruby. Using a linter that was able to recognize these pitfalls and encourage me to correct them has been wonderful. Who knew that linters could be such valuable learning resources?

If you’re still looking to explore new programming languages, I would highly recommend checking out Exercism. It has been a great resource so far, and I definitely plan to use it when learning new programming languages going forward.