Seven Languages in Seven Weeks

Ruby💎

yield

In Ruby, the yield keyword is used within a method definition to transfer control from the method to a block that is passed to it. Essentially, it allows a method to invoke a block of code that is provided by the caller.

Here’s a simple example to illustrate how yield works:

1
2
3
4
5
6
7
8
9
def greet
  puts "Hello"
  yield if block_given?
  puts "Goodbye"
end

greet do
  puts "World"
end

In this example, the great method prints “Hello”, then executes the block that is passed to it (puts "World"), and finally prints “Goodbye”. The yield keyword inside the greet method transfers contrrol to the block provided by the caller (puts "World"), and once the block finishes executing, control returns to the greet method to execute the rest of its code (puts "Goodbye").

The yield keyword is particularly useful for defining methods that perform some common behavior but allow customization of certain parts of the behavior through a block. This makes the method more flexible and reusable, as it can adapt its behavior based on the block provided by the caller.

E.g., you could use yield to define a method that performs some processing on a collection of items, with the specific processing logic defined by the caller bia a block:

1
2
3
4
5
6
7
8
9
def process_items(items)
  items.each do |item|
    yield item
  end
end

process_items([1, 2, 3]) do |item|
  puts "Processing item: #{item}"
end

In this example, the process_item method iterates over each item in the items array and yields control to the block provided by the caller, passing each item to the block. This allows the caller to define custom processing logic for each item.

yield comparison

Speaking of yield, Python also has yield keyword, however, the usage is slightly different.

In Python, yield is used to define a generator function, which is a special type of function that can yield multiple values lazily instead of returning them all at once. When a generator function encounters a yield statement, it temporarily suspends its execution and yields a value to the caller. The next time the generator function is called, it resumes execution from where it left off, maintaining its local state.

E.g. in Python:

1
2
3
4
5
6
7
8
9
def count_up_to(n):
  count = 1
  while count <= n:
    yield count
    count += 1

# Using the generator function
for num in count_up_to(5):
  print(num)

In this example, the count_to_up function is a generator function that yields numbers from 1 up to n. When the generator function is called in a for loop, it yields each number one at a time until the loop terminates.

While both Ruby and Python have a yield keyword, they serve different purposes:

  • In Ruby, yield is used to transfer conrtrol from a method to a block, provided by the caller.
  • In Python, yield is used to define a generator function, allowing it to yield values lazily.

Despite these differences, both lanuages use the yield keywords to enable powerful and flexible programming patterns.

Wrapping Up

To an industry that grew up embracing the C family of languages including C++, C#, Java, and others, Ruby is a breath os fresh air.

Core Strengths

Ruby’s pure OO allows you to treat objects in a uniform and constant way. The duck typing allows truer polymorphic designs based on what an object can support rather than that object’s inheritance hierarchy. And Ruby’s modules and open classes let a programmer attach behavior to syntax that goes beyond the typical method or instance variable definitions in a class.

Ruby is ideal as a scripting language, or as a web dev language if the scaling requirements are reasonable. The language is intensely productive. Some of the features that enable that productivity make Ruby hard to compile and make the performance suffer.

Scripting

Ruby is a fantastic scripting language. Writing glue code to munge two applications together, writing a spider to scrape web pages for a stock quote or book price, or running local build environments or automated tests are excellent uses for Ruby.

As a language with a presence on most major OS, Ruby is a good choice for scripting environments. The language has a wide variety of libraries included with the base, as well as thousands of gems, or prepackaged plug-ins, that can be used for loading CSV files, processing XML, or working with low-level Internet APIs.

Web Development

Rails is already one of the most successful web development frameworks of all time. The design is based on well-understood model-view-controller paradigms. The many naming conventions for database and application elements allow a typical application to be built with few lines of configuration at all. And the framework has plug-ins than handle some difficult production issues:

  • The structure of Rails applications is always consistent and well understood.
  • Migrations handle changes in the database schema.
  • Several well-documented conventions reduce the total amount of configuration code.
  • Many different plug-ins are available.

Weaknesses

No language is perfect for all applications. Ruby has its share of limitations too.

Performance

Ruby’s primary weakness is performance. Sure, Ruby is getting faster. Version 1.9 is up to ten times faster for some use cases. A new Ruby virtual machine written by Evan Phoenix called Rubinius has the potential to compile Ruby using a JIT compiler. This approach looks at an interpreter’s uasge patterns for a block of code to anticipate which code is likely to be needed again. This approach works well for Ruby, a lang where syntax clues are usually not enough to allow compilation. Remember, the definition of a class can change at any time.

Many of the language’s feature such as open classes, duck typing, and method_missing defeat the very tools that enable compilation and the associated performance gains.

Concurrency and OOP

OOP has a critical limitation in that it doesn’t handle concurrency well. Ruby’s threads are not native threads. They are green threads, which are managed by the Ruby interpreter. This means that Ruby threads are not good for parallel processing. Ruby’s threads are good for I/O-bound tasks, but not for CPU-bound tasks.

The whole premise of the model depends on wrapping behavior around state, and usually the state can be changed. This programming strategy leads to serious problems w/ concurrency. At best, OO systems are next to impossible to debug and cannot be reliably tested for concurrent environments.

Type Safety

Ruby is a dynamically typed language. This means that the type of a variable is not checked until the program is run. This can lead to runtime errors that would be caught by a compiler in a statically typed language. While dynamic typing can be very flexible and productive, it can also lead to subtle bugs that are difficult to track down.

With Duck typing, you can generally have cleaner abstractions with concise, readable code. But duck typing comes at a price, too. Static typing allows a whole range of tools that makes it easier to do syntax trees and thus provide integrated development environments. IDEs for Ruby are more difficult to build.

Conclusion

Ruby’s core strengths are its syntax and flexibility. The core weaknesses are around performance, though the performance is reasonable for many purposes. All in all, Ruby is an excellent language for OOP. For the right applications, Ruby can excel. As with any tool, use it to solve the right set of problems, and you’re not likely to be disappointed. And keep your eyes open for a little magic along the way.