May 30, 2019·5 min read·engineeringrubypatternsgc

Ruby 2.7 preview adds patterns for structured values

Ruby 2.7 preview 1 introduces experimental pattern matching and manual heap compaction. Both changes make existing structure easier to work with, in source and in memory.

Reading a nested value often requires a sequence of questions before the useful data can be extracted. Is this a hash, does it contain the expected name, and does its children array contain the shape the next operation needs? Ruby 2.7.0 preview 1, released May 30, introduces experimental pattern matching to express that structural requirement and bind the interesting parts.

The release is a preview intended to gather feedback before the planned December final release. Its pattern-matching syntax is explicitly experimental. That status matters because this is an opportunity to examine how the feature fits Ruby, without treating every detail of the preview as a permanent language guarantee.

I like Ruby when the relationship between data and the operation on it is clear from the code. Blocks and collection methods already support that style. Structural patterns offer another way to state what an operation needs, especially when a value combines hashes and arrays.

Matching and extracting can share one description

The announcement demonstrates matching a parsed JSON value with symbolized keys. A small variation shows the same mechanism without requiring external input.

record = {
  name: "Morgan",
  children: [{name: "Sam", age: 7}]
}

case record
in {name: "Morgan", children: [{name: "Sam", age: age}]}
  puts age
else
  puts "different structure"
end

The pattern includes literal values that must match and a variable that receives a matched value. Its nesting describes the relationship between the outer record and the child entry. The branch body can then use the extracted age without repeating the entire navigation through the structure.

This is useful when the branch decision and extraction describe the same condition. An ordinary conditional can check one path and accidentally read another. Keeping both in one structural expression reduces that duplication. It also gives a reviewer a single place to inspect the expected shape.

The example does not establish that every possible record is valid. It describes one branch of a decision about values. Other inputs need an appropriate alternative or failure policy, and application-specific constraints still need to be stated. A matched age value is not automatically a valid age for every domain operation.

A useful example should therefore include an input that takes the alternative branch. That reveals whether the structural test excludes what the caller intended to exclude. For instance, changing the child name in this example should select the other branch. The application can then decide whether that means ignoring the record, returning an error, or processing a different kind of record. The pattern itself does not choose among those policies.

There is also a distinction between matching a structure and designing a data model. A pattern can make a nested hash easier to consume, but it does not decide whether several fields represent one entity or several independent concepts. Those choices determine which structure the program produces before matching begins.

The proposal history exposes the object-model question

Kazuki Tsujimoto's pattern-matching proposal explores how decomposition should interact with Ruby objects. The early discussion asks whether matching should identify a class as well as extract its contents, and how duck typing fits the design. Participants also challenge fragile patterns where the interpretation is surprising.

That is a substantive language question. Ruby normally lets objects participate in behavior through methods rather than through a closed list of structural declarations. Pattern matching has to decide which aspects of an object are available for decomposition and what the resulting pattern promises. A design borrowed from a language with a different data model cannot simply assume those choices away.

The historical examples in the proposal are therefore design discussion, not a replacement manual for the shipped preview syntax. They show the pressures that led to the feature. The preview's own example is the appropriate reference for the syntax demonstrated here.

I find the experimental period useful for this reason. The question is whether a pattern communicates an object's relevant structure predictably. A short expression that saves typing but surprises a reader about what matched would weaken the readability that makes Ruby attractive.

Compaction addresses where live objects reside

The same preview introduces GC.compact, documented in the release announcement and the manual-compaction proposal. It compacts live heap objects so that fewer pages may be needed. The announcement identifies fragmentation in some multithreaded Ruby programs as a source of higher memory use and degraded speed.

Collection and compaction answer different questions. Determining that an object is no longer needed can free its space. If remaining live objects occupy scattered positions, the resulting free space can still be fragmented. Compaction concerns how those live objects occupy the available heap after their liveness is known.

The method gives a program an explicit compaction operation. Its existence does not imply that calling it continuously is an appropriate policy. Moving objects is work, and the reason to perform it follows the memory organization of a particular workload. The preview supplies the operation so that its behavior can be examined and improved.

The release also describes the possibility of a more copy-on-write-friendly heap. That is a statement about page organization, not a promise that every application's memory use decreases by a fixed amount. The number and placement of live objects determine what compaction has an opportunity to improve.

Interactive work receives a separate improvement

The preview updates IRB with multiline editing through Reline, a Ruby implementation compatible with Readline. It also adds RDoc integration, letting an interactive session display references for classes, modules, and methods. Those changes concern the process of exploring the language rather than the behavior of a deployed application.

That is a useful complement to experimental syntax. A developer can examine representative values and try a structural pattern in an interactive environment that supports editing more than one line. Documentation access in the same workflow reduces the distance between an unfamiliar operation and its reference material.

Ruby 2.7 preview 1 therefore offers concrete material for feedback on several different kinds of work. The pattern example tests how a structural requirement reads, while GC.compact exposes a memory-management operation with workload-dependent effects. Neither requires assuming the December release has already settled the preview's experimental language choices.