Functional Ruby

Gem Version Travis CI Build Status AppVeyor Build status Coverage Status Code Climate Inline docs Dependency Status License

A gem for adding functional programming tools to Ruby. Inspired by Erlang, Clojure, and Functional Java.

Introduction

Two things I love are Ruby and functional programming. If you combine Ruby's ability to create functions sans-classes with the power of blocks, proc, and lambda, Ruby code can follow just about every modern functional programming design paradigm. Add to this Ruby's vast metaprogramming capabilities and Ruby is easily one of the most powerful languages in common use today.

Goals

Our goal is to implement various functional programming patterns in Ruby. Specifically:

Features

The primary site for documentation is the automatically generated API documentation.

Supported Ruby Versions

MRI 2.0 and higher, JRuby (1.9 mode), and Rubinius 2.x. This library is pure Ruby and has no gem dependencies. It should be fully compatible with any interpreter that is compliant with Ruby 2.0 or newer.

Install

gem install functional-ruby

or add the following line to Gemfile:

gem 'functional-ruby'

and run bundle install from your shell.

Once you've installed the gem you must require it in your project:

require 'functional'

Examples

Specifying a protocol:

Functional::SpecifyProtocol(:Name) do
  attr_accessor :first
  attr_accessor :middle
  attr_accessor :last
  attr_accessor :suffix
end

Defining immutable data structures including Either, Option, Union and Record

Name = Functional::Record.new(:first, :middle, :last, :suffix) do
  mandatory :first, :last
  default :first, 'J.'
  default :last, 'Doe'
end

anon = Name.new #=> #<record Name :first=>"J.", :middle=>nil, :last=>"Doe", :suffix=>nil>
matz = Name.new(first: 'Yukihiro', last: 'Matsumoto') #=> #<record Name :first=>"Yukihiro", :middle=>nil, :last=>"Matsumoto", :suffix=>nil>

Pattern matching using protocols, type checking, and other options:

class Foo
  include Functional::PatternMatching
  include Functional::Protocol
  include Functional::TypeCheck

  def greet
    return 'Hello, World!'
  end

  defn(:greet, _) do |name|
    "Hello, #{name}!"
  end

  defn(:greet, _) { |name|
    "Pleased to meet you, #{name.full_name}!"
  }.when {|name| Type?(name, CustomerModel, ClientModel) }

  defn(:greet, _) { |name|
    "Hello, #{name.first} #{name.last}!"
  }.when {|name| Satisfy?(name, :Name) }

  defn(:greet, :doctor, _) { |name|
    "Hello, Dr. #{name}!"
  }

  defn(:greet, nil, _) { |name|
    "Goodbye, #{name}!"
  }

  defn(:greet, _, _) { |_, name|
    "Hello, #{name}!"
  }
end

Performance improvement of idempotent functions through memoization:

class Factors
  include Functional::Memo

  def self.sum_of(number)
    of(number).reduce(:+)
  end

  def self.of(number)
    (1..number).select {|i| factor?(number, i)}
  end

  def self.factor?(number, potential)
    number % potential == 0
  end

  memoize(:sum_of)
  memoize(:of)
end

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

License and Copyright

Functional Ruby is free software released under the MIT License.