Ruby 2.4 simplifies integers and expands case mapping
Ruby 2.4 exposes one Integer class while retaining native representation predicates. Unicode case mapping also changes assumptions about length, round trips and ASCII-only processing.
Ruby's public numeric model has exposed a storage distinction through Fixnum and Bignum. Code that wanted an integer could encounter either class according to magnitude. Ruby 2.4 removes those visible classes in favor of Integer. The stable release also expands familiar case-conversion methods beyond ASCII, which simplifies some application code while changing assumptions that older code may have relied on.
The December 25 announcement identifies 2.4.0 as the first stable release in the series. Its two headline changes affect opposite sides of the abstraction boundary. Integer unification hides a representation detail from Ruby code. Unicode case mapping makes text operations account for distinctions that an ASCII-only implementation ignored.
I find Ruby's consistent object model and readable syntax useful for larger object-oriented programs. One Integer class fits that preference. The text changes need a more qualified judgment, because a concise method name such as downcase cannot by itself describe every rule a protocol or language requires.
Integer becomes the concrete public class
The release NEWS file explains that Integer changes from an abstract class to a concrete class. For example, 0.class now returns Integer. Fixnum and Bignum remain constants bound to Integer, so some existing source continues to execute while seeing the unified behavior.
0.class == Integer
(2 ** 100).class == Integer
1.kind_of?(Fixnum) == 1.kind_of?(Integer)
These expressions illustrate the public class relationship. They do not establish that every integer has the same internal representation. Native extensions still distinguish the small representation with FIXNUM_P(obj) and the Bignum representation with RB_TYPE_P(obj, T_BIGNUM). The feature macro RUBY_INTEGER_UNIFICATION identifies the new arrangement at the C level.
The C constants rb_cFixnum and rb_cBignum are removed. An extension referring to them can fail to compile, which makes native compatibility work part of adopting the release. The correct repair depends on intent. An extension asking whether a value is an integer needs a different test from one that will inspect a particular internal representation.
Pure Ruby code can have a subtler problem. A kind_of?(Fixnum) condition now has the meaning of kind_of?(Integer). If its purpose was to impose a numeric bound, continuing to run is insufficient. That bound should become an explicit value check. Otherwise the compatibility constant preserves the spelling while changing the branch's accepted values.
This is a useful simplification of the public model because callers doing arithmetic do not usually need storage categories. Native code retains the information needed for representation-specific operations. The two layers therefore become more distinct, with a single application-level class and explicit predicates where the implementation still matters.
Case mapping no longer stops at ASCII
String and Symbol uppercase, lowercase, capitalization and case-swapping methods now support Unicode mappings. The release notes list UTF-8, UTF-16BE and LE, UTF-32BE and LE, and the ISO-8859 family as supported encodings for non-ASCII mapping. The corresponding String bang methods receive the new behavior as well.
The tagged String documentation gives important limits. With no option, case conversion uses full Unicode mapping suitable for most languages, but context-dependent mappings are not supported. A :turkic option adapts mappings for Turkic languages, including the relationship between uppercase I and lowercase dotless i.
The :lithuanian option is documented as currently providing ordinary full Unicode mapping. The language-specific behavior described as a future intention should not be mistaken for implemented behavior in this release. An option's presence does not establish that every intended special case already exists.
The :ascii option restricts conversion to ASCII letters and cannot be combined with another option. That preserves a concrete operation for code whose requirements are deliberately narrower than Unicode case mapping. The NEWS file specifically calls out programs processing Unicode data under rules that still require ASCII-only conversion.
"HELLO".downcase(:ascii)
"I".downcase(:turkic)
"Straße".upcase
These calls request different policies. Restricting the affected characters differs from choosing the Turkic mapping or using the default Unicode behavior. Choosing among them requires knowing what the text represents, rather than assuming that every string should use the same casing rule.
Length and normalization assumptions can change
The String documentation warns that Unicode case conversion can change both character length and byte length. An implementation that allocates output space based on an assumption of one output character per input character can therefore be wrong. Even pure Ruby code can encode that assumption indirectly when it pairs positions in the original text with positions in the converted result.
Round-trip assumptions also need care. The documentation explicitly says that a lowercase string need not equal the result of uppercasing and then lowercasing it. Case mapping is an operation defined by character rules, not a reversible encoding. Treating it as reversible can lose distinctions the input contained.
Unicode normalization is another independent property. The release's documentation says case mapping does not necessarily preserve normalization. An application that requires normalized output needs to make that requirement explicit after the transformation. Case conversion alone does not establish it.
downcase and downcase! additionally support :fold, a case-folding operation described as more far-reaching than ordinary case mapping. It cannot currently combine with another option, and the documentation specifically notes the absence of a Turkic variant for that combination. Case folding and display-oriented case conversion should therefore be chosen according to the operation being performed.
I value having the policy visible at the call site. A program that handles human text benefits from broader character support, while a protocol parser may require a narrower transformation. Ruby 2.4 provides options for that distinction, but it cannot determine the application's text rules from the receiver's String class.
Predicate APIs reduce unnecessary match state
Ruby 2.4 also adds Regexp#match?. It answers a Boolean matching question without creating the back-reference object or changing $~. String and Symbol gain corresponding predicate methods in the release notes. This makes a common intent explicit when the caller does not need capture data.
A matching predicate has a narrower effect than an operation used to retrieve a MatchData result. That matters in code which retains match state from an earlier operation. The new predicate avoids overwriting that state solely to decide whether another pattern matches. The announcement also identifies reduced allocation as a motivation, without giving an application-wide performance guarantee.
Array minimum and maximum operations receive another allocation improvement under specified conditions. The implementation can avoid constructing a temporary array for suitable literal expressions. The NEWS file also warns that Array now has its own maximum and minimum methods, so redefining Enumerable's method no longer necessarily changes Array behavior. That is a compatibility consequence of a concrete implementation improvement.
New methods can meet existing library methods
The release introduces Array and Enumerable sum, but the NEWS file points out that libraries already define methods with that name. ActiveSupport, facets and simple_stats are named examples. The new implementation is mostly compatible with existing uses, yet cannot be perfectly compatible with every library's behavior.
This is a different kind of compatibility issue from the Integer C constants. No missing method is required. The interpreter can now supply a method that a library previously supplied, and differences in its contract can affect callers. A short method name shared across implementations does not establish that their edge cases agree.
Hash receives transform_values and compact, along with mutating variants. These methods name operations that previously required more general iteration. A value transformation keeps the relationship between a key and its transformed value explicit, while compaction provides a separate operation for removing absent values. They also place more behavior in the core collection API, which is relevant when a library already supplies similarly named helpers.
For interactive debugging, binding.irb starts a session at a program binding. The announcement compares it with the binding.pry workflow. This gives a developer an interpreter-provided way to inspect values and execute Ruby code in that context instead of adding successive print statements. Its role is distinct from thread failure reporting, because an interactive session requires a deliberate stop at a chosen binding while a thread exception report concerns a failure that has already occurred.
The release also improves thread debugging with exception-reporting controls and deadlock reports that include backtraces and dependencies. OpenSSL 1.1.0 support arrives while support for 0.9.7 and earlier is dropped. These changes affect debugging and runtime integration alongside the language's public model. The stable release gives each change a versioned reference, so application and gem compatibility can be discussed against the actual 2.4.0 behavior, including the native integer predicates and the documented limits of Unicode mapping.