0.0
No commit activity in last 3 years
No release in over 3 years
Common optics for Ruby
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 0
>= 0
 Project Readme

ruby-optics

Simple library with common functional optics for Ruby.

Supported Ruby versions

This library officially supports the following Ruby versions:

  • MRI >= 2.3.0

Installation

Add this to your Gemfile

gem 'ruby-optics'

and then run

bundle install

Or install it manually

gem install ruby-optics

Usage

Lens - is an object which encapsulates getter/setter functionality for immutable data in composable way. Any lens consist of two functions set and get. get describes how can retrieve a value from a whole object, and set - how to update this value without mutating the object.

Example for getting/setting value by key from hash:

def hash_get(key)
  -> (hash) { hash[key] }
end

def hash_set(key)
  -> (new_value, hash) { hash.merge(key => new_value) }
end

To create lens you need to specify both these functions:

lens = Lens.new(hash_get(:foo), hash_set(:foo))

Then you can use the lens to retrieve, set and modify value by the key in arbitrary hash:

object = { foo: 1, bar: 2 }

lens.get(object)
=> 2

lens.set(3, object)
=> { foo: 3, bar: 2 }

lens.modify(object) { |foo| foo * 10 }
=> { foo: 10, bar: 2 }

object
=> { foo: 1, bar: 2 }

Note that set and modify operations don't mutate original value, thus keeping it unchanged.

The real power of lens comes from their compositional properties, which allows to creates lenses that works with arbitrary nested objects by composing them from simpler ones.

foo_lens = Lens.new(hash_get(:foo), hash_set(:foo))
bar_lens = Lens.new(hash_get(:bar), hash_set(:bar))

lens = foo_lens.compose_lens(bar_lens)

object = { foo: { bar: 1 } }

lens.get(object)
=> 1

lens.set(3, object)
=> { foo: { bar: 3 } }

lens.modify(object) { |foo| foo * 10 }
=> { foo: { bar: 10 } }

object
=> { foo: { bar: 1 } }

The library provides lens constructors for common cases, so you don't need to composes lenses yourself

lens = Optics.hash_lens(:foo, :bar)

License

See LICENSE file.