[
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n  pull_request:\n\njobs:\n  test:\n\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        ruby-version:\n          - 3.2\n          - 3.1\n          - \"3.0\"\n\n    steps:\n      - uses: actions/checkout@v3\n      - name: Set up Ruby ${{ matrix.ruby-version }}\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: ${{ matrix.ruby-version }}\n          bundler-cache: true # runs 'bundle install' and caches installed gems automatically\n      - name: Run tests\n        run: bundle exec rake\n"
  },
  {
    "path": ".gitignore",
    "content": "/.bundle/\n/.yardoc\n/Gemfile.lock\n/_yardoc/\n/coverage/\n/doc/\n/pkg/\n/spec/reports/\n/tmp/\n*.bundle\n*.so\n*.o\n*.a\nmkmf.log\n*.gem\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: ruby\n\ndist: trusty\n\nmatrix:\n  include:\n    - rvm: 2.1\n    - rvm: 2.2\n    - rvm: 2.3.3\n    - rvm: 2.4.0\n    - rvm: ruby-head\n    - rvm: jruby-head\n      env: \"JRUBY_OPTS=--debug\"\n      before_install:\n        - gem install bundler -v'1.13.7'\n    - rvm: jruby-9.1.7.0\n      env: \"JRUBY_OPTS=--debug\"\n\nscript:\n  - rake\n"
  },
  {
    "path": "Gemfile",
    "content": "source 'https://rubygems.org'\n\n# Specify your gem's dependencies in kleisli.gemspec\ngemspec\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "Copyright (c) 2014 Josep M. Bach, Ryan Levick\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Kleisli [![Build Status](https://secure.travis-ci.org/txus/kleisli.svg)](http://travis-ci.org/txus/kleisli)\n\nAn idiomatic, clean implementation of a few common useful monads in Ruby,\nwritten by [Ryan Levick][rylev] and me.\n\nIt aims to be idiomatic Ruby to use in Enter-Prise production apps, not a proof\nof concept.\n\nIn your Gemfile:\n\n```ruby\ngem 'kleisli'\n```\n\nWe would like to thank Curry and Howard for their correspondence.\n\n## Notation\n\nFor all its monads, Kleisli implements `return` (we call it `lift` instead, as\n`return` is a reserved keyword in Ruby) with convenience global methods (see\nwhich for each monad below).\n\nKleisli uses a clever Ruby syntax trick to implement the `bind` operator, which\nlooks like this: `>->` when used with a block. We will probably burn in hell\nfor this. You can also use `>` or `>>` if you're going to pass in a proc or\n lambda object.\n\n`Maybe` and `Either` are applicative functors with the apply operator `*`. Read\nfurther to see how it works.\n\n### Function composition\n\nYou can use Haskell-like function composition with F and the familiar `.`. This\nis such a perversion of Ruby syntax that Matz would probably condemn this:\n\nThink of `F` as the identity function. Although it's just a hack to make it\nwork in Ruby.\n\n```ruby\n# Reminder that (f . g) x= f(g(x))\nf = F . first . last\nf.call [[1,2], [3,4]]\n# => 3\n\nf = F . capitalize . reverse\nf.call \"hello\"\n# => \"Olleh\"\n```\n\nFunctions and methods are interchangeable:\n\n```ruby\nfoo = lambda { |s| s.reverse }\n\nf = F . capitalize . fn(&foo)\nf.call \"hello\"\n# => \"Olleh\"\n```\n\nAll functions and methods are partially applicable:\n\n```ruby\n\n# Partially applied method:\nf = F . split(\":\") . strip\nf.call \"  localhost:9092     \"\n# => [\"localhost\", \"9092\"]\n\n# Partially applied lambda:\nmy_split = lambda { |str, *args| str.split(*args) }\nf = F . fn(\":\", &my_split) . strip\nf.call \"  localhost:9092     \"\n# => [\"localhost\", \"9092\"]\n```\n\nFinally, for convenience, `F` is the identity function:\n\n```ruby\nF.call(1) # => 1\n```\n\n## Maybe monad\n\nThe Maybe monad is useful to express a pipeline of computations that might\nreturn nil at any point. `user.address.street` anyone?\n\n### `>->` (bind)\n\n```ruby\nrequire \"kleisli\"\n\nmaybe_user = Maybe(user) >-> user {\n  Maybe(user.address) } >-> address {\n    Maybe(address.street) }\n\n# If user exists\n# => Some(\"Monad Street\")\n# If user is nil\n# => None()\n\n# You can also use Some and None as type constructors yourself.\nx = Some(10)\ny = None()\n\n```\n\nAs usual (with Maybe and Either), using point-free style is much cleaner:\n\n```ruby\nMaybe(user) >> F . fn(&Maybe) . address >> F . fn(&Maybe) . street\n```\n\n### `fmap`\n\n```ruby\nrequire \"kleisli\"\n\n# If we know that a user always has an address with a street\nMaybe(user).fmap(&:address).fmap(&:street)\n\n# If the user exists\n# => Some(\"Monad Street\")\n# If the user is nil\n# => None()\n```\n\n### `*` (applicative functor's apply)\n\n```ruby\nrequire \"kleisli\"\n\nadd = -> x, y { x + y }\nSome(add) * Some(10) * Some(2)\n# => Some(12)\nSome(add) * None() * Some(2)\n# => None\n```\n\n## Try\n\nThe Try monad is useful to express a pipeline of computations that might throw\nan exception at any point.\n\n### `>->` (bind)\n\n```ruby\nrequire \"kleisli\"\n\njson_string = get_json_from_somewhere\n\nresult = Try { JSON.parse(json_string) } >-> json {\n  Try { json[\"dividend\"].to_i / json[\"divisor\"].to_i }\n}\n\n# If no exception was thrown:\n\nresult       # => #<Try::Success @value=123>\nresult.value # => 123\n\n# If there was a ZeroDivisionError exception for example:\n\nresult           # => #<Try::Failure @exception=#<ZeroDivisionError ...>>\nresult.exception # => #<ZeroDivisionError ...>\n```\n\n### `fmap`\n\n```ruby\nrequire \"kleisli\"\n\nTry { JSON.parse(json_string) }.fmap(&:symbolize_keys).value\n\n# If everything went well:\n# => { :my => \"json\", :with => \"symbolized keys\" }\n# If an exception was thrown:\n# => nil\n```\n\n### `to_maybe`\n\nSometimes it's useful to interleave both `Try` and `Maybe`. To convert a `Try`\ninto a `Maybe` you can use `to_maybe`:\n\n```ruby\nrequire \"kleisli\"\n\nTry { JSON.parse(json_string) }.fmap(&:symbolize_keys).to_maybe\n\n# If everything went well:\n# => Some({ :my => \"json\", :with => \"symbolized keys\" })\n# If an exception was thrown:\n# => None()\n```\n\n### `to_either`\n\nSometimes it's useful to interleave both `Try` and `Either`. To convert a `Try`\ninto a `Either` you can use `to_either`:\n\n```ruby\nrequire \"kleisli\"\n\nTry { JSON.parse(json_string) }.fmap(&:symbolize_keys).to_either\n\n# If everything went well:\n# => Right({ :my => \"json\", :with => \"symbolized keys\" })\n# If an exception was thrown:\n# => Left(#<JSON::ParserError: 757: unexpected token at 'json'>)\n```\n\n## Either\n\nThe Either monad is useful to express a pipeline of computations that might return an error object with some information.\n\nIt has two type constructors: `Right` and `Left`. As a useful mnemonic, `Right` is for when everything went \"right\" and `Left` is used for errors.\n\nThink of it as exceptions without messing with the call stack.\n\n### `>->` (bind)\n\n```ruby\nrequire \"kleisli\"\n\nresult = Right(3) >-> value {\n  if value > 1\n    Right(value + 3)\n  else\n    Left(\"value was less or equal than 1\")\n  end\n} >-> value {\n  if value % 2 == 0\n    Right(value * 2)\n  else\n    Left(\"value was not even\")\n  end\n}\n\n# If everything went well\nresult # => Right(12)\nresult.value # => 12\n\n# If it failed in the first block\nresult # => Left(\"value was less or equal than 1\")\nresult.value # => \"value was less or equal than 1\"\n\n# If it failed in the second block\nresult # => Left(\"value was not even\")\nresult.value # => \"value was not even\"\n\n# Point-free style bind!\nresult = Right(3) >> F . fn(&Right) . *(2)\nresult # => Right(6)\nresult.value # => 6\n```\n\n### `fmap`\n\n```ruby\nrequire \"kleisli\"\n\nresult = if foo > bar\n  Right(10)\nelse\n  Left(\"wrong\")\nend.fmap { |x| x * 2 }\n\n# If everything went well\nresult # => Right(20)\n# If it didn't\nresult # => Left(\"wrong\")\n```\n\n### `*` (applicative functor's apply)\n\n```ruby\nrequire \"kleisli\"\n\nadd = -> x, y { x + y }\nRight(add) * Right(10) * Right(2)\n# => Right(12)\nRight(add) * Left(\"error\") * Right(2)\n# => Left(\"error\")\n```\n\n### `or`\n\n`or` does pretty much what would you expect:\n\n```ruby\nrequire 'kleisli'\n\nRight(10).or(Right(999)) # => Right(10)\nLeft(\"error\").or(Left(\"new error\")) # => Left(\"new error\")\nLeft(\"error\").or { |err| Left(\"new #{err}\") } # => Left(\"new error\")\n```\n\n### `to_maybe`\n\nSometimes it's useful to turn an `Either` into a `Maybe`. You can use\n`to_maybe` for that:\n\n```ruby\nrequire \"kleisli\"\n\nresult = if foo > bar\n  Right(10)\nelse\n  Left(\"wrong\")\nend.to_maybe\n\n# If everything went well:\nresult # => Some(10)\n# If it didn't\nresult # => None()\n```\n\n## Future\n\nThe Future monad models a pipeline of computations that will happen in the future, as soon as the value needed for each step is available. It is useful to model, for example, a sequential chain of HTTP calls.\n\nThere's a catch unfortunately -- values passed to the functions are wrapped in\nlambdas, so you need to call `.call` on them. See the examples below.\n\n### `>->` (bind)\n\n```ruby\nrequire \"kleisli\"\n\nf = Future(\"myendpoint.com\") >-> url {\n  Future { HTTP.get(url.call) }\n} >-> response {\n  Future {\n    other_url = JSON.parse(response.call.body)[:other_url]\n    HTTP.get(other_url)\n  }\n} >-> other_response {\n  Future { JSON.parse(other_response.call.body) }\n}\n\n# Do some other stuff...\n\nf.await # => block until the whole pipeline is realized\n# => { \"my\" => \"response body\" }\n```\n\n### `fmap`\n\n```ruby\nrequire \"kleisli\"\n\nFuture { expensive_operation }.fmap { |x| x * 2 }.await\n# => result of expensive_operation * 2\n```\n\n## Who's this\n\nThis was made by [Josep M. Bach (Txus)](http://blog.txus.io) and [Ryan\nLevick][rylev] under the MIT license. We are [@txustice][twitter] and\n[@itchyankles][itchyankles] on twitter (where you should probably follow us!).\n\n[twitter]: https://twitter.com/txustice\n[itchyankles]: https://twitter.com/itchyankles\n[rylev]: https://github.com/rylev\n"
  },
  {
    "path": "Rakefile",
    "content": "require \"bundler/gem_tasks\"\nrequire \"rake/testtask\"\n\nRake::TestTask.new do |t|\n  t.libs << \"test\"\n  t.test_files = FileList['test/**/*_test.rb']\n  t.verbose = true\nend\n\ntask :default => :test\n"
  },
  {
    "path": "kleisli.gemspec",
    "content": "# coding: utf-8\nlib = File.expand_path('../lib', __FILE__)\n$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)\nrequire 'kleisli/version'\n\nGem::Specification.new do |spec|\n  spec.name          = \"kleisli\"\n  spec.version       = Kleisli::VERSION\n  spec.authors       = [\"Josep M. Bach\", \"Ryan Levick\"]\n  spec.email         = [\"josep.m.bach@gmail.com\", \"ryan.levick@gmail.com\"]\n  spec.summary       = %q{Usable, idiomatic common monads in Ruby}\n  spec.description   = %q{Usable, idiomatic common monads in Ruby}\n  spec.homepage      = \"https://github.com/txus/kleisli\"\n  spec.license       = \"MIT\"\n\n  spec.files         = `git ls-files -z`.split(\"\\x0\")\n  spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }\n  spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})\n  spec.require_paths = [\"lib\"]\n\n  spec.add_development_dependency \"bundler\", \"~> 1.6\"\n  spec.add_development_dependency \"rake\", \"~> 10.0\"\n  spec.add_development_dependency \"minitest\", \"~> 5.5\"\nend\n"
  },
  {
    "path": "lib/kleisli/composition.rb",
    "content": "module Kleisli\n  class ComposedFn < BasicObject\n    def self.comp(f, g)\n      lambda { |*args| f[g[*args]] }\n    end\n\n    def initialize(fns=[])\n      @fns = fns\n    end\n\n    def fn(*args, &block)\n      f = -> arguments, receiver {\n        block.call(receiver, *arguments)\n      }.curry[args]\n      ComposedFn.new(@fns + [f])\n    end\n\n    def method_missing(meth, *args, &block)\n      f = -> arguments, receiver {\n        receiver.send(meth, *arguments, &block)\n      }.curry[args]\n      ComposedFn.new(@fns + [f])\n    end\n\n    def call(*args)\n      if @fns.any?\n        @fns.reduce { |f, g| ComposedFn.comp(f, g) }.call(*args)\n      else\n        args.first\n      end\n    end\n\n    def to_ary\n      @fns.to_ary\n    end\n  end\nend\n\nF = Kleisli::ComposedFn.new\n"
  },
  {
    "path": "lib/kleisli/either.rb",
    "content": "require 'kleisli/monad'\nrequire 'kleisli/maybe'\n\nmodule Kleisli\n  class Either < Monad\n    attr_reader :right, :left\n\n    def ==(other)\n      right == other.right && left == other.left\n    end\n\n    def *(other)\n      self >-> f {\n        other >-> val {\n          Right(f.arity > 1 ? f.curry.call(val) : f.call(val))\n        }\n      }\n    end\n\n    class Right < Either\n      alias value right\n\n      def initialize(right)\n        @right = right\n      end\n\n      def >(f)\n        f.call(@right)\n      end\n\n      def fmap(&f)\n        Right.new(f.call(@right))\n      end\n\n      def to_maybe\n        Maybe::Some.new(@right)\n      end\n\n      def or(other=nil, &other_blk)\n        self\n      end\n\n      def to_s\n        \"Right(#{@right.inspect})\"\n      end\n      alias inspect to_s\n    end\n\n    class Left < Either\n      alias value left\n\n      def initialize(left)\n        @left = left\n      end\n\n      def >(f)\n        self\n      end\n\n      def fmap(&f)\n        self\n      end\n\n      def to_maybe\n        Maybe::None.new\n      end\n\n      def or(other=self, &other_blk)\n        if other_blk\n          other_blk.call(@left)\n        else\n          other\n        end\n      end\n\n      def to_s\n        \"Left(#{@left.inspect})\"\n      end\n      alias inspect to_s\n    end\n  end\nend\n\nRight = Kleisli::Either::Right.method(:new)\nLeft = Kleisli::Either::Left.method(:new)\n\ndef Right(v)\n  Kleisli::Either::Right.new(v)\nend\n\ndef Left(v)\n  Kleisli::Either::Left.new(v)\nend\n"
  },
  {
    "path": "lib/kleisli/functor.rb",
    "content": "module Kleisli\n  class Functor\n    def fmap(&f)\n      raise NotImplementedError, \"this functor doesn't implement fmap\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/kleisli/future.rb",
    "content": "require 'kleisli/monad'\n\nmodule Kleisli\n  class Future < Monad\n    def self.lift(v=nil, &block)\n      if block\n        new(Thread.new(&block))\n      else\n        new(Thread.new { v })\n      end\n    end\n\n    def initialize(t)\n      @t = t\n    end\n\n    def >(f)\n      f.call(-> { await })\n    end\n\n    def fmap(&f)\n      Future.lift(f.call(-> { await }))\n    end\n\n    def await\n      @t.join.value\n    end\n  end\nend\n\ndef Future(v=nil, &block)\n  Kleisli::Future.lift(v, &block)\nend\n"
  },
  {
    "path": "lib/kleisli/maybe.rb",
    "content": "require 'kleisli/monad'\n\nmodule Kleisli\n  class Maybe < Monad\n    attr_reader :value\n\n    def self.lift(value)\n      if value.nil?\n        None.new\n      else\n        Some.new(value)\n      end\n    end\n\n    def ==(other)\n      value == other.value\n    end\n\n    def *(other)\n      self >-> f {\n        f = f.to_proc\n        other >-> val {\n          Maybe.lift(f.arity > 1 ? f.curry.call(val) : f.call(val))\n        }\n      }\n    end\n\n    class None < Maybe\n      def fmap(&f)\n        self\n      end\n\n      def >(block)\n        self\n      end\n\n      def or(other=self, &other_blk)\n        if other_blk\n          other_blk.call\n        else\n          other\n        end\n      end\n\n      def to_s\n        \"None\"\n      end\n      alias inspect to_s\n    end\n\n    class Some < Maybe\n      def initialize(value)\n        @value = value\n      end\n\n      def fmap(&f)\n        Maybe.lift(f.call(@value))\n      end\n\n      def >(block)\n        block.call(@value)\n      end\n\n      def or(other=nil, &other_blk)\n        self\n      end\n\n      def to_s\n        \"Some(#{@value.inspect})\"\n      end\n      alias inspect to_s\n    end\n  end\nend\n\nMaybe = Kleisli::Maybe.method(:lift)\n\ndef Maybe(v)\n  Maybe.(v)\nend\n\ndef None()\n  Maybe(nil)\nend\n\ndef Some(v)\n  Maybe(v)\nend\n"
  },
  {
    "path": "lib/kleisli/monad.rb",
    "content": "require \"kleisli/functor\"\n\nmodule Kleisli\n  class Monad < Functor\n    def >(block)\n      raise NotImplementedError, \"this monad doesn't implement >->\"\n    end\n\n    def >>(block)\n      self > block\n    end\n  end\nend\n"
  },
  {
    "path": "lib/kleisli/try.rb",
    "content": "require 'kleisli/monad'\nrequire 'kleisli/maybe'\n\nmodule Kleisli\n  class Try < Monad\n    attr_reader :exception, :value\n\n    def self.lift(f)\n      Success.new(f.call)\n    rescue => e\n      Failure.new(e)\n    end\n\n    class Success < Try\n      def initialize(value)\n        @value = value\n      end\n\n      def >(f)\n        f.call(@value)\n      rescue => e\n        Failure.new(e)\n      end\n\n      def fmap(&f)\n        Try { f.call(@value) }\n      end\n\n      def to_maybe\n        Maybe::Some.new(@value)\n      end\n\n      def to_either\n        Either::Right.new(@value)\n      end\n    end\n\n    class Failure < Try\n      def initialize(exception)\n        @exception = exception\n      end\n\n      def >(f)\n        self\n      end\n\n      def fmap(&f)\n        self\n      end\n\n      def to_maybe\n        Maybe::None.new\n      end\n\n      def to_either\n        Either::Left.new(@exception)\n      end\n    end\n  end\nend\n\ndef Try(&f)\n  Kleisli::Try.lift(f)\nend\n"
  },
  {
    "path": "lib/kleisli/version.rb",
    "content": "module Kleisli\n  VERSION = \"0.2.7\"\nend\n"
  },
  {
    "path": "lib/kleisli.rb",
    "content": "require \"kleisli/version\"\nrequire \"kleisli/maybe\"\nrequire \"kleisli/try\"\nrequire \"kleisli/future\"\nrequire \"kleisli/either\"\nrequire \"kleisli/composition\"\n\nmodule Kleisli\nend\n"
  },
  {
    "path": "test/kleisli/composition_test.rb",
    "content": "require 'test_helper'\n\nclass CompositionTest < Minitest::Test\n  def test_one_method\n    f = F . first\n    result = f.call([1])\n    assert Fixnum === result, \"#{result} is not a number\"\n    assert_equal 1, result\n  end\n\n  def test_two_methods\n    f = F . first . last\n    result = f.call([1, [2,3]])\n    assert Fixnum === result, \"#{result} is not a number\"\n    assert_equal 2, result\n  end\n\n  def test_one_function\n    my_first = lambda { |x| x.first }\n\n    f = F . fn(&my_first)\n    result = f.call([1])\n    assert Fixnum === result, \"#{result} is not a number\"\n    assert_equal 1, result\n  end\n\n  def test_two_functions\n    my_first = lambda { |x| x.first }\n    my_last = lambda { |x| x.last }\n\n    f = F . fn(&my_first) . fn(&my_last)\n    result = f.call([1, [2,3]])\n    assert Fixnum === result, \"#{result} is not a number\"\n    assert_equal 2, result\n  end\n\n  def test_one_function_one_block\n    my_last = lambda { |x| x.last }\n\n    f = F . fn { |x| x.first } . fn(&my_last)\n    result = f.call([1, [2,3]])\n    assert Fixnum === result, \"#{result} is not a number\"\n    assert_equal 2, result\n  end\n\n  def test_one_function_one_method\n    my_last = lambda { |x| x.last }\n\n    f = F . first . fn(&my_last)\n    result = f.call([1, [2,3]])\n    assert Fixnum === result, \"#{result} is not a number\"\n    assert_equal 2, result\n  end\n\n  def test_undefined_method\n    f = F . foo\n    assert_raises(NoMethodError) { f.call(1) }\n  end\n\n  def test_identity\n    assert_equal 1, F.call(1)\n  end\n\n  def test_partially_applied_method\n    f = F . split(\":\")\n    result = f.call(\"localhost:9092\")\n    assert Array === result, \"#{result} is not an array\"\n    assert_equal [\"localhost\", \"9092\"], result\n  end\n\n  def test_partially_applied_fn\n    split = lambda { |x, *args| x.split(*args) }\n    f = F . fn(\":\", &split)\n    result = f.call(\"localhost:9092\")\n    assert Array === result, \"#{result} is not an array\"\n    assert_equal [\"localhost\", \"9092\"], result\n  end\nend\n"
  },
  {
    "path": "test/kleisli/either_test.rb",
    "content": "require 'test_helper'\n\nclass EitherTest < Minitest::Test\n  def test_lift_right\n    assert_equal 3, Right(3).value\n  end\n\n  def test_lift_left\n    assert_equal \"error\", Left(\"error\").value\n  end\n\n  def test_bind_right\n    v = Right(1) >-> x {\n      if x == 1\n        Right(x + 90)\n      else\n        Left(\"FAIL\")\n      end\n    }\n    assert_equal Right(91), v\n  end\n\n  def test_bind_left\n    v = Left(\"error\") >-> x {\n      Right(x * 20)\n    }\n    assert_equal Left(\"error\"), v\n  end\n\n  def test_fmap_right\n    assert_equal Right(2), Right(1).fmap { |x| x * 2 }\n  end\n\n  def test_fmap_left\n    assert_equal Left(\"error\"), Left(\"error\").fmap { |x| x * 2 }\n  end\n\n  def test_to_maybe_right\n    assert_equal Some(2), Right(1).fmap { |x| x * 2 }.to_maybe\n  end\n\n  def test_to_maybe_left\n    assert_equal None(), Left(\"error\").fmap { |x| x * 2 }.to_maybe\n  end\n\n  def test_pointfree\n    assert_equal Right(10), Right(5) >> F . fn(&Right) . *(2)\n  end\n\n  def test_applicative_functor_right_arity_1\n    assert_equal Right(20), Right(-> x { x * 2 }) * Right(10)\n  end\n\n  def test_applicative_functor_right_arity_2\n    assert_equal Right(20), Right(-> x, y { x * y }) * Right(10) * Right(2)\n  end\n\n  def test_applicative_functor_left\n    assert_equal Left(\"error\"), Right(-> x, y { x * y }) * Left(\"error\") * Right(2)\n  end\nend\n"
  },
  {
    "path": "test/kleisli/future_test.rb",
    "content": "require 'test_helper'\n\nclass FutureTest < Minitest::Test\n  def test_immediate_value\n    assert_equal 30, Future(30).await\n  end\n\n  def test_simple_future_executes_in_parallel\n    str = \"\"\n    Future { sleep 0.1; str << \"bar\" }.tap {\n      str << \"foo\"\n    }.await\n    assert_equal \"foobar\", str\n  end\n\n  def test_bind\n    f = Future(30) >-> n {\n      Future { n.call * 2 }\n    } >-> n {\n      Future { n.call * 2 } >-> m {\n        Future(m.call + 2)\n      }\n    }\n    assert_equal 122, f.await\n  end\n\n  def test_fmap\n    f = Future(30).fmap { |x| x.call * 2 }\n    assert_equal 60, f.await\n  end\nend\n"
  },
  {
    "path": "test/kleisli/maybe_test.rb",
    "content": "require 'test_helper'\n\nclass MaybeTest < Minitest::Test\n  def test_unwrapping_some\n    assert_equal 3, Some(3).value\n  end\n\n  def test_unwrapping_none\n    assert_equal nil, None().value\n  end\n\n  def test_bind_none\n    assert_equal None(), None() >> F . fn(&Maybe) . *(2)\n  end\n\n  def test_bind_some\n    assert_equal Some(6), Some(3) >> F . fn(&Maybe) . *(2)\n  end\n\n  def test_fmap_none\n    assert_equal None(), None().fmap { |x| x * 2 }\n  end\n\n  def test_fmap_some\n    assert_equal Some(6), Some(3).fmap { |x| x * 2 }\n  end\n\n  def test_applicative_functor_some_arity_1\n    assert_equal Some(20), Maybe(-> x { x * 2 }) * Maybe(10)\n  end\n\n  def test_applicative_functor_some_arity_2\n    assert_equal Some(20), Maybe(-> x, y { x * y }) * Maybe(10) * Maybe(2)\n  end\n\n  def test_applicative_functor_none\n    assert_equal None(), Maybe(-> x, y { x * y }) * None() * Maybe(2)\n  end\nend\n"
  },
  {
    "path": "test/kleisli/try_test.rb",
    "content": "require 'test_helper'\n\nclass TryTest < Minitest::Test\n  def test_success\n    assert_equal 2, Try { 10 / 5 }.value\n  end\n\n  def test_failure\n    assert_kind_of ZeroDivisionError, Try { 10 / 0 }.exception\n  end\n\n  def test_to_maybe_success\n    assert_equal Some(2), Try { 10 / 5 }.to_maybe\n  end\n\n  def test_to_maybe_failure\n    assert_equal None(), Try { 10 / 0 }.to_maybe\n  end\n\n  def test_to_either_success\n    assert_equal Right(2), Try { 10 / 5 }.to_either\n  end\n\n  def test_to_either_failure\n    assert_kind_of ZeroDivisionError, Try { 10 / 0 }.to_either.left\n  end\n\n  def test_fmap_success\n    assert_equal 4, Try { 10 / 5 }.fmap { |x| x * 2 }.value\n  end\n\n  def test_fmap_failure\n    assert_kind_of ZeroDivisionError, Try { 10 / 0 }.fmap { |x| x * 2 }.exception\n  end\n\n  def test_bind\n    try = Try { 20 / 10 } >-> number { Try { 10 / number } }\n    assert_equal 5, try.value\n  end\nend\n"
  },
  {
    "path": "test/test_helper.rb",
    "content": "require 'kleisli'\nrequire 'minitest'\nrequire 'minitest/autorun'\n"
  }
]