Declarative Programming in Ruby

Randall Reed, Jr.
Def Method
Published in
3 min readMar 7, 2016

--

I’ve been programming in Ruby for almost two years now, and sometimes I forget how hard it was for me to wrap my head around the Ruby approach to solving problems. Finally, Tyler Mcginnis has given me the vocabulary to describe the paradigm shift I experienced — Imperative vs Declarative code.

With imperative code, you’re telling your program how to do something. And with declarative code, you’re telling your program what you want to do.

-Tyler Mcginnis, React Fundamentals

Coming from a C++ background, I was used to imperative programming; those mental pathways were well-trodden. Although Ruby can be used to write imperative code, its expressiveness makes declarative code possible. The ‘each’ method was a revelation to someone who had been writing ‘for’ and ‘while’ loops for years.

Example: Performing an operation on every element of an array

  • Imperative Anti-pattern: Manually keeping track of array index
i = 0while i < array.length do  puts array[i]end
  • Declarative alternative: #each
array.each do |element|  puts elementend

However, Ruby doesn’t stop there. If you still find yourself defaulting to using each for every problem and using local variables to keep track of state, consider whether a simpler solution exists. Here are some anti-patterns to watch for, along with iterators that will make your code more declarative.

Anti-patterns in Ruby Iterators

Creating a new array from an existing array

  • Desired return type: Array
  • Imperative Anti-pattern: Manually adding elements to new array
squares = []array.each do | element | squares << element ** 2endsquares
array.collect { | element | element ** 2 }

Returning a subset of an array satisfying criterion

  • Desired return type: Array
  • Imperative Anti-pattern: Manually adding elements to new array
odds = []array.each do | element | odds << element if element.odd?endodds
array.select { | element | element.odd? }

Returning the first element of an array satisfying a certain criterion

  • Desired return type: Object (whatever class the element is)
  • Imperative Anti-pattern: Using a local variable to store element
first_positive = 0array.each do | element | first_positive = element if element > 0endfirst_positive
array.detect { | element | element > 0 }

Determining the number of elements of an array satisfying a certain criterion

  • Desired return type: Integer
  • Imperative Anti-pattern: Manually incrementing a counter
match_count = 0array.each do | element | match_count += 1 if element % 10 == 0endmatch_count
  • Declarative alternative: #count
array.count { | element | element % 10 == 0 }

Determining whether any elements of an array share a certain characteristic

  • Desired return type: Boolean
  • Imperative Anti-pattern: Manually toggling a boolean flag
is_present = falsearray.each do | element | is_present = true if element < 0endis_present
  • Declarative alternative: #any?
array.any? do | element | element < 0end

Determining whether all elements of an array share a certain characteristic

  • Desired return type: Boolean
  • Imperative Anti-pattern: Manually toggling a boolean flag
all_even = truearray.each do | element | all_even = all_even && element.even?endall_even
array.all? do | element | element.even?end

Conclusion

Once you’ve gotten the hang of declarative programming, imperative programming begins to feel a bit like micro-managing. Learn to let the details go; stop worrying about managing state and put Ruby’s iterators to work for you.

--

--

Randall Reed, Jr.
Def Method

Learning Ruby and building stuff. Developer at @degreed, based in Richmond, VA.