Neiro | Functional programming, software architecture
14 Feb 2016

Immutable collections in Ruby with Hamster

Ruby has much in common with functional programming languages. For example, Ruby supports high-order functions, lambdas, currying and recursion, but not the immutability - Ruby’s types and data structures are mutable and can be changed at any time.

Why immutability is important? The’re many arguments for that:

If you want to make Ruby hash immutable, you can use freeze it:

immutable = { foo: 'bar' } immutable.freeze

immutable[:foo] = 'tball'# RuntimeError: can't modify frozen Hash

But if you want to use already immutable collections, sets and other data structures, you can try Hamster library.

1 Hamster

Hamster provides efficient, immutable and thread-safe collection classes for Ruby, such as Hash, Vector, Set, SortedSet and List. Hamster collections offers Ruby`s Hash, Array, Enumberable compatibility where it possible. You can require all of Hamster collection classes:

gem i hamster
require 'hamster'

or only certain types:

require 'hamster/hash'
require 'hamster/vector'
require 'hamster/set'
require 'hamster/sorted_set'
require 'hamster/list'
require 'hamster/deque'

2 Hash

# Create new Hamster Hash
parrot = Hamster::Hash[type: 'bird', class: 'parrot', color: 'yellow']

parrot.get(:color) # yellow
parrot[:type] # bird

# You can not change hash because of immutability
parrot[:subclass] = 'budgie' # NoMethodError: undefined method `[]='
# But you can create a new

budgie = parrot.put :subclass, 'budgie'
budgie == parrot # false
budgie[:subclass] # budgie
parrot[:subclass] # nil

budgie.to_hash.class # Plain Ruby Hash

3 List

list = Hamster::List[0, 1, 2] list.tail # Hamster::List[1, 2]
list.head # 0

4 Set

# Hamster's set is an unordered collection with no duplicates
colors = Hamster::Set[:green, :white]
colors.include?(:green) # true

palette = colors.add :yellow # Hamster::Set[:green, :yellow, :white]
colors.include?(:yellow) # false palette.superset?(colors) # true
palette.intersection(colors) # Hamster::Set[:green, :white]
palette.difference(colors).first # :yellow

palette.to_a # Plain Ruby array: [:green, :white, :yellow]

5 Vector

 # Vector is an integer-indexed immutable array
vector = Hamster::Vector[0, 1, 2]
vector[2] # 2
vector[-1] # 2
vector[-4] # nil
vector[1,2] # Hamster::Vector[1, 2]
vector[0..2] # Hamster::Vector[0, 1, 2]

binary_vector = vector.delete_at 0 # Hamster::Vector[1, 2]
vector.size # 3
binary_vector.size # 2
vector == binary_vector # false

(binary_vector + vector).sort.uniq # Hamster::Vector[0, 1, 2]
(binary_vector + vector).sort.uniq == vector # true

6 Conclusion

If you want to use immutable data structures in Ruby to write more reliable, efficient and at the same time thread-safe code, you can take a look at Hamster. You can find more in Hamster’s API documentation.

Tags: ruby functional immutable