{"id":16577293,"url":"https://github.com/bolshakov/fear","last_synced_at":"2025-09-26T12:35:08.434Z","repository":{"id":27167382,"uuid":"30636917","full_name":"bolshakov/fear","owner":"bolshakov","description":"Ruby port of some Scala's monads","archived":false,"fork":false,"pushed_at":"2024-08-12T02:06:06.000Z","size":628,"stargazers_count":63,"open_issues_count":5,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-12T22:10:26.381Z","etag":null,"topics":["either","monads","option","pattern-matching","ruby","try"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bolshakov.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-02-11T07:56:08.000Z","updated_at":"2024-10-05T23:13:53.000Z","dependencies_parsed_at":"2023-02-13T10:46:04.294Z","dependency_job_id":"2d266f13-c3b5-4820-b92c-54f2509353d5","html_url":"https://github.com/bolshakov/fear","commit_stats":{"total_commits":295,"total_committers":12,"mean_commits":"24.583333333333332","dds":0.5152542372881356,"last_synced_commit":"7e61e93a6ec2f2cf68bb3653d5b4208da8a5cfb9"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bolshakov%2Ffear","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bolshakov%2Ffear/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bolshakov%2Ffear/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bolshakov%2Ffear/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bolshakov","download_url":"https://codeload.github.com/bolshakov/fear/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247526763,"owners_count":20953143,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["either","monads","option","pattern-matching","ruby","try"],"created_at":"2024-10-11T22:10:24.348Z","updated_at":"2025-09-26T12:35:08.094Z","avatar_url":"https://github.com/bolshakov.png","language":"Ruby","readme":"# Fear\n[![Gem Version](https://badge.fury.io/rb/fear.svg)](https://badge.fury.io/rb/fear)\n[![Spec](https://github.com/bolshakov/fear/actions/workflows/spec.yml/badge.svg)](https://github.com/bolshakov/fear/actions/workflows/spec.yml)\n[![Maintainability](https://api.codeclimate.com/v1/badges/01030620c59e9f40961b/maintainability)](https://codeclimate.com/github/bolshakov/fear/maintainability)\n[![Coverage Status](https://coveralls.io/repos/github/bolshakov/fear/badge.svg?branch=master)](https://coveralls.io/github/bolshakov/fear?branch=master)\n\nThis gem provides `Option`, `Either`, and `Try` monads implemented an idiomatic way. \n It is highly inspired by scala's implementation. \n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'fear'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install fear\n\n## Usage\n\n* [Option](#option-api-documentation) \n* [Try](#try-api-documentation)\n* [Either](#either-api-documentation)\n* [Future](#future-api-documentation)\n* [For composition](#for-composition-api-documentation)\n* [Pattern Matching](#pattern-matching-api-documentation)\n\n### Option ([API Documentation](https://www.rubydoc.info/github/bolshakov/fear/master/Fear/Option))\n\nRepresents optional (nullable) values. Instances of `Option` are either an instance of \n`Some` or the object `None`.\n\nThe most idiomatic way to use an `Option` instance is to treat it as a collection\n\n```ruby\nname = Fear.option(params[:name]) \nupper = name.map(\u0026:strip).select { |n| n.length != 0 }.map(\u0026:upcase)\nputs upper.get_or_else('')\n```\n\nThis allows for sophisticated chaining of `Option` values without\nhaving to check for the existence of a value.\n\nA less-idiomatic way to use `Option` values is via pattern matching\n\n```ruby\ncase Fear.option(params[:name])\nin Fear::Some(name) \n  name.strip.upcase\nin Fear::None\n  'No name value'\nend\n```\n\nor manually checking for non emptiness\n\n```ruby\nname = Fear.option(params[:name])\nif name.empty?\n puts 'No name value'\nelse\n puts name.strip.upcase\nend\n```\n\nAlternatively, you can use camel-case factory methods `Fear::Option()`, `Fear::Some()` and `Fear::None` methods:\n\n```ruby\nFear::Option(42) #=\u003e #\u003cFear::Some get=42\u003e\nFear::Option(nil) #=\u003e #\u003cFear::None\u003e\n\nFear::Some(42) #=\u003e #\u003cFear::Some get=42\u003e\nFear::Some(nil) #=\u003e #\u003cFear::Some get=nil\u003e\nFear::None #=\u003e #\u003cFear::None\u003e\n``` \n\n#### Option#get_or_else\n\nReturns the value from this `Some` or evaluates the given default argument if this is a `None`.\n\n```ruby\nFear.some(42).get_or_else { 24/2 } #=\u003e 42\nFear.none.get_or_else { 24/2 }   #=\u003e 12\n\nFear.some(42).get_or_else(12)  #=\u003e 42\nFear.none.get_or_else(12)    #=\u003e 12\n```\n\n#### Option#or_else\n\nreturns self `Some` or the given alternative if this is a `None`.\n\n```ruby\nFear.some(42).or_else { Fear.some(21) } #=\u003e Fear.some(42)\nFear.none.or_else { Fear.some(21) }   #=\u003e Fear.some(21)\nFear.none.or_else { None }     #=\u003e None\n```\n\n#### Option#include?\n\nChecks if `Option` has an element that is equal (as determined by `==`) to given values.\n\n```ruby\nFear.some(17).include?(17) #=\u003e true\nFear.some(17).include?(7)  #=\u003e false\nFear.none.include?(17)   #=\u003e false\n```\n\n#### Option#each\n\nPerforms the given block if this is a `Some`.\n\n```ruby\nFear.some(17).each { |value| puts value } #=\u003e prints 17\nFear.none.each { |value| puts value } #=\u003e does nothing\n```\n\n#### Option#map \n\nMaps the given block to the value from this `Some` or returns self if this is a `None`\n\n```ruby\nFear.some(42).map { |v| v/2 } #=\u003e Fear.some(21)\nFear.none.map { |v| v/2 }   #=\u003e None\n```\n\n#### Option#flat_map\n\nReturns the given block applied to the value from this `Some` or returns self if this is a `None`\n\n```ruby\nFear.some(42).flat_map { |v| Fear.some(v/2) }   #=\u003e Fear.some(21)\nFear.none.flat_map { |v| Fear.some(v/2) }     #=\u003e None\n```\n\n#### Option#any?\n\nReturns `false` if `None` or returns the result of the application of the given predicate to the `Some` value.\n\n```ruby \nFear.some(12).any? { |v| v \u003e 10 }  #=\u003e true\nFear.some(7).any? { |v| v \u003e 10 }   #=\u003e false\nFear.none.any? { |v| v \u003e 10 }    #=\u003e false\n```\n\n#### Option#select\n\nReturns self if it is nonempty and applying the predicate to this `Option`'s value returns `true`. Otherwise, \nreturn `None`.\n\n```ruby\nFear.some(42).select { |v| v \u003e 40 } #=\u003e Fear.some(42)\nFear.some(42).select { |v| v \u003c 40 } #=\u003e None\nFear.none.select { |v| v \u003c 40 }   #=\u003e None\n```\n\n#### Option#filter_map\n\nReturns a new `Some` of truthy results (everything except `false` or `nil`) of\nrunning the block or `None` otherwise.\n\n```ruby\nFear.some(42).filter_map { |v| v/2 if v.even? } #=\u003e Fear.some(21)\nFear.some(42).filter_map { |v| v/2 if v.odd? } #=\u003e Fear.none\nFear.some(42).filter_map { |v| false } #=\u003e Fear.none\nFear.none.filter_map { |v| v/2 }   #=\u003e Fear.none\n```\n\n#### Option#reject\n\nReturns `Some` if applying the predicate to this `Option`'s value returns `false`. Otherwise, return `None`.\n\n```ruby \nFear.some(42).reject { |v| v \u003e 40 } #=\u003e None\nFear.some(42).reject { |v| v \u003c 40 } #=\u003e Fear.some(42)\nFear.none.reject { |v| v \u003c 40 }   #=\u003e None\n```\n\n#### Option#get\n\nNot an idiomatic way of using Option at all. Returns values of raise `NoSuchElementError` error if option is empty.\n   \n#### Option#empty?\n\nReturns `true` if the `Option` is `None`, `false` otherwise.\n\n```ruby\nFear.some(42).empty? #=\u003e false\nFear.none.empty?   #=\u003e true\n```\n\n#### Option#blank?\n\nReturns `true` if the `Option` is `None`, `false` otherwise.\n\n```ruby\nFear.some(42).blank? #=\u003e false\nFear.none.blank?   #=\u003e true\n```\n\n#### Option#present?\n\nReturns `false` if the `Option` is `None`, `true` otherwise.\n\n```ruby\nFear.some(42).present? #=\u003e true\nFear.none.present?   #=\u003e false\n```\n\n#### Option#zip\n\nReturns a `Fear::Some` formed from this Option and another Option by combining the corresponding elements in a pair. \nIf either of the two options is empty, `Fear::None` is returned.\n\n```ruby\nFear.some(\"foo\").zip(Fear.some(\"bar\")) #=\u003e Fear.some([\"foo\", \"bar\"])\nFear.some(\"foo\").zip(Fear.some(\"bar\")) { |x, y| x + y } #=\u003e Fear.some(\"foobar\")\nFear.some(\"foo\").zip(Fear.none) #=\u003e Fear.none\nFear.none.zip(Fear.some(\"bar\")) #=\u003e Fear.none\n\n```\n\n@see https://github.com/scala/scala/blob/2.11.x/src/library/scala/Option.scala\n \n\n### Try ([API Documentation](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Try))\n\nThe `Try` represents a computation that may either result\nin an exception, or return a successfully computed value. Instances of `Try`,\nare either an instance of `Success` or `Failure`.\n\nFor example, `Try` can be used to perform division on a\nuser-defined input, without the need to do explicit\nexception-handling in all of the places that an exception\nmight occur.\n\n```ruby\ndividend = Fear.try { Integer(params[:dividend]) }\ndivisor = Fear.try { Integer(params[:divisor]) }\nproblem = dividend.flat_map { |x| divisor.map { |y| x / y } }\n\ncase problem\nin Fear::Success(result)\n  puts \"Result of #{dividend.get} / #{divisor.get} is: #{result}\"\nin Fear::Failure(ZeroDivisionError)\n  puts \"Division by zero is not allowed\"\nin Fear::Failure(exception)\n  puts \"You entered something wrong. Try again\"\n  puts \"Info from the exception: #{exception.message}\"\nend\n```\n\nAn important property of `Try` shown in the above example is its\nability to _pipeline_, or chain, operations, catching exceptions\nalong the way. The `flat_map` and `map` combinators in the above\nexample each essentially pass off either their successfully completed\nvalue, wrapped in the `Success` type for it to be further operated\nupon by the next combinator in the chain, or the exception wrapped\nin the `Failure` type usually to be simply passed on down the chain.\nCombinators such as `recover_with` and `recover` are designed to provide some\ntype of default behavior in the case of failure.\n\n*NOTE*: Only non-fatal exceptions are caught by the combinators on `Try`.\nSerious system errors, on the other hand, will be thrown.\n\nAlternatively, include you can use camel-case factory method `Fear::Try()`:\n\n```ruby\nFear::Try { 4/0 }  #=\u003e #\u003cFear::Failure exception=...\u003e\nFear::Try { 4/2 }  #=\u003e #\u003cFear::Success value=2\u003e\n```\n\n#### Try#get_or_else\n\nReturns the value from this `Success` or evaluates the given default argument if this is a `Failure`.\n\n```ruby\nFear.success(42).get_or_else { 24/2 }                #=\u003e 42\nFear.failure(ArgumentError.new).get_or_else { 24/2 } #=\u003e 12\n```\n\n#### Try#include?\n\nReturns `true` if it has an element that is equal given values, `false` otherwise.\n\n```ruby\nFear.success(17).include?(17)                #=\u003e true\nFear.success(17).include?(7)                 #=\u003e false\nFear.failure(ArgumentError.new).include?(17) #=\u003e false\n```\n\n#### Try#each\n\nPerforms the given block if this is a `Success`. If block raise an error, \nthen this method may raise an exception.\n\n```ruby\nFear.success(17).each { |value| puts value }  #=\u003e prints 17\nFear.failure(ArgumentError.new).each { |value| puts value } #=\u003e does nothing\n```\n\n#### Try#map\n\nMaps the given block to the value from this `Success` or returns self if this is a `Failure`.\n\n```ruby\nFear.success(42).map { |v| v/2 }                 #=\u003e Fear.success(21)\nFear.failure(ArgumentError.new).map { |v| v/2 }  #=\u003e Fear.failure(ArgumentError.new)\n```\n\n#### Try#flat_map\n\nReturns the given block applied to the value from this `Success`or returns self if this is a `Failure`.\n\n```ruby\nFear.success(42).flat_map { |v| Fear.success(v/2) } #=\u003e Fear.success(21)\nFear.failure(ArgumentError.new).flat_map { |v| Fear.success(v/2) } #=\u003e Fear.failure(ArgumentError.new)\n```\n\n#### Try#to_option\n\nReturns an `Some` containing the `Success` value or a `None` if this is a `Failure`.\n\n```ruby\nFear.success(42).to_option                 #=\u003e Fear.some(42)\nFear.failure(ArgumentError.new).to_option  #=\u003e None\n```\n\n#### Try#any?\n\nReturns `false` if `Failure` or returns the result of the application of the given predicate to the `Success` value.\n\n```ruby\nFear.success(12).any? { |v| v \u003e 10 }                #=\u003e true\nFear.success(7).any? { |v| v \u003e 10 }                 #=\u003e false\nFear.failure(ArgumentError.new).any? { |v| v \u003e 10 } #=\u003e false\n```\n\n#### Try#success? and Try#failure?\n\n\n```ruby\nFear.success(12).success? #=\u003e true\nFear.success(12).failure? #=\u003e true\n\nFear.failure(ArgumentError.new).success? #=\u003e false\nFear.failure(ArgumentError.new).failure? #=\u003e true\n```\n\n#### Try#get\n\nReturns the value from this `Success` or raise the exception if this is a `Failure`.\n\n```ruby\nFear.success(42).get                 #=\u003e 42\nFear.failure(ArgumentError.new).get  #=\u003e ArgumentError: ArgumentError\n```\n\n#### Try#or_else\n\nReturns self `Try` if it's a `Success` or the given alternative if this is a `Failure`.\n\n```ruby\nFear.success(42).or_else { Fear.success(-1) }                 #=\u003e Fear.success(42)\nFear.failure(ArgumentError.new).or_else { Fear.success(-1) }  #=\u003e Fear.success(-1)\nFear.failure(ArgumentError.new).or_else { Fear.try { 1/0 } }  #=\u003e Fear.failure(ZeroDivisionError.new('divided by 0'))\n```\n\n#### Try#flatten\n\nTransforms a nested `Try`, ie, a `Success` of `Success`, into an un-nested `Try`, ie, a `Success`.\n\n```ruby\nFear.success(42).flatten                         #=\u003e Fear.success(42)\nFear.success(Fear.success(42)).flatten                #=\u003e Fear.success(42)\nFear.success(Fear.failure(ArgumentError.new)).flatten #=\u003e Fear.failure(ArgumentError.new)\nFear.failure(ArgumentError.new).flatten { -1 }   #=\u003e Fear.failure(ArgumentError.new)\n```\n\n#### Try#select\n\nConverts this to a `Failure` if the predicate is not satisfied.\n\n```ruby\nFear.success(42).select { |v| v \u003e 40 }\n  #=\u003e Fear.success(42)\nFear.success(42).select { |v| v \u003c 40 }\n  #=\u003e Fear.failure(Fear::NoSuchElementError.new(\"Predicate does not hold for 42\"))\nFear.failure(ArgumentError.new).select { |v| v \u003c 40 }\n  #=\u003e Fear.failure(ArgumentError.new)\n```\n\n#### Recovering from errors\n\nThere are two ways to recover from the error. `Try#recover_with` method  is like `flat_map` for the exception. And \nyou can pattern match against the error!\n\n```ruby\nFear.success(42).recover_with do |m|\n  m.case(ZeroDivisionError) { Fear.success(0) }\nend #=\u003e Fear.success(42)\n\nFear.failure(ArgumentError.new).recover_with do |m|\n  m.case(ZeroDivisionError) { Fear.success(0) }\n  m.case(ArgumentError) { |error| Fear.success(error.class.name) }\nend #=\u003e Fear.success('ArgumentError')\n```\n\nIf the block raises error, this new error returned as an result\n\n```ruby\nFear.failure(ArgumentError.new).recover_with do\n  raise\nend #=\u003e Fear.failure(RuntimeError)\n```\n\nThe second possibility for recovery is `Try#recover` method. It is like `map` for the exception. And it's also heavely\nrelies on pattern matching.\n\n```ruby\nFear.success(42).recover do |m|\n  m.case(\u0026:message)\nend #=\u003e Fear.success(42)\n\nFear.failure(ArgumentError.new).recover do |m|\n  m.case(ZeroDivisionError) { 0 }\n  m.case(\u0026:message)\nend #=\u003e Fear.success('ArgumentError')\n```\n\nIf the block raises an error, this new error returned as an result\n\n```ruby\nFear.failure(ArgumentError.new).recover do |m|\n  raise\nend #=\u003e Fear.failure(RuntimeError)\n```\n\n#### Try#to_either\n\nReturns `Left` with exception if this is a `Failure`, otherwise returns `Right` with `Success` value.\n\n```ruby\nFear.success(42).to_either                #=\u003e Fear.right(42)\nFear.failure(ArgumentError.new).to_either #=\u003e Fear.left(ArgumentError.new)\n```\n\n### Either ([API Documentation](https://www.rubydoc.info/github/bolshakov/fear/master/Fear/Either))\n  \nRepresents a value of one of two possible types (a disjoint union.)\nAn instance of `Either` is either an instance of `Left` or `Right`.\n\nA common use of `Either` is as an alternative to `Option` for dealing\nwith possible missing values.  In this usage, `None` is replaced\nwith a `Left` which can contain useful information.\n`Right` takes the place of `Some`. Convention dictates\nthat `Left` is used for failure and `Right` is used for Right.\n\nFor example, you could use `Either\u003cString, Fixnum\u003e` to `#select_or_else` whether a\nreceived input is a +String+ or an +Fixnum+.\n\n```ruby\nin = Readline.readline('Type Either a string or an Int: ', true)\nresult = begin\n  Fear.right(Integer(in))\nrescue ArgumentError\n  Fear.left(in)\nend\n\ncase result\nin Fear::Right(x)\n  \"You passed me the Int: #{x}, which I will increment. #{x} + 1 = #{x+1}\"\nin Fear::Left(x)\n  \"You passed me the String: #{x}\"\nend\n```\n\nEither is right-biased, which means that `Right` is assumed to be the default case to\noperate on. If it is `Left`, operations like `#map`, `#flat_map`, ... return the `Left` value\nunchanged.\n\nAlternatively, you can use camel-case factory methods `Fear::Left()`, and `Fear::Right()`:\n\n```ruby\nFear::Left(42)  #=\u003e #\u003cFear::Left value=42\u003e\nFear::Right(42)  #=\u003e #\u003cFear::Right value=42\u003e\n```\n\n#### Either#get_or_else\n\nReturns the value from this `Right` or evaluates the given default argument if this is a `Left`.\n\n```ruby\nFear.right(42).get_or_else { 24/2 }         #=\u003e 42\nFear.left('undefined').get_or_else { 24/2 } #=\u003e 12\n\nFear.right(42).get_or_else(12)         #=\u003e 42\nFear.left('undefined').get_or_else(12) #=\u003e 12\n```\n\n#### Either#or_else\n\nReturns self `Right` or the given alternative if this is a `Left`.\n\n```ruby\nFear.right(42).or_else { Fear.right(21) }           #=\u003e Fear.right(42)\nFear.left('unknown').or_else { Fear.right(21) }     #=\u003e Fear.right(21)\nFear.left('unknown').or_else { Fear.left('empty') } #=\u003e Fear.left('empty')\n```\n\n#### Either#include?\n\nReturns `true` if `Right` has an element that is equal to given value, `false` otherwise.\n\n```ruby\nFear.right(17).include?(17)         #=\u003e true\nFear.right(17).include?(7)          #=\u003e false\nFear.left('undefined').include?(17) #=\u003e false\n```\n\n#### Either#each\n\nPerforms the given block if this is a `Right`.\n\n```ruby\nFear.right(17).each { |value| puts value } #=\u003e prints 17\nFear.left('undefined').each { |value| puts value } #=\u003e does nothing\n```\n\n#### Either#map\n\nMaps the given block to the value from this `Right` or returns self if this is a `Left`.\n\n```ruby\nFear.right(42).map { |v| v/2 }          #=\u003e Fear.right(21)\nFear.left('undefined').map { |v| v/2 }  #=\u003e Fear.left('undefined')\n```\n\n#### Either#flat_map\n\nReturns the given block applied to the value from this `Right` or returns self if this is a `Left`.\n\n```ruby\nFear.right(42).flat_map { |v| Fear.right(v/2) }         #=\u003e Fear.right(21)\nFear.left('undefined').flat_map { |v| Fear.right(v/2) } #=\u003e Fear.left('undefined')\n```\n\n#### Either#to_option\n\nReturns an `Some` containing the `Right` value or a `None` if this is a `Left`.\n\n```ruby\nFear.right(42).to_option          #=\u003e Fear.some(42)\nFear.left('undefined').to_option  #=\u003e Fear::None\n```\n\n#### Either#any?\n\nReturns `false` if `Left` or returns the result of the application of the given predicate to the `Right` value.\n\n```ruby\nFear.right(12).any? { |v| v \u003e 10 }         #=\u003e true\nFear.right(7).any? { |v| v \u003e 10 }          #=\u003e false\nFear.left('undefined').any? { |v| v \u003e 10 } #=\u003e false\n```\n\n#### Either#right?, Either#success?\n\nReturns `true` if this is a `Right`, `false` otherwise.\n\n```ruby\nFear.right(42).right?   #=\u003e true\nFear.left('err').right? #=\u003e false\n```\n\n#### Either#left?, Either#failure?\n\nReturns `true` if this is a `Left`, `false` otherwise.\n\n```ruby\nFear.right(42).left?   #=\u003e false\nFear.left('err').left? #=\u003e true\n```\n\n#### Either#select_or_else\n\nReturns `Left` of the default if the given predicate does not hold for the right value, otherwise, \nreturns `Right`.\n\n```ruby\nFear.right(12).select_or_else(-1, \u0026:even?)       #=\u003e Fear.right(12)\nFear.right(7).select_or_else(-1, \u0026:even?)        #=\u003e Fear.left(-1)\nFear.left(12).select_or_else(-1, \u0026:even?)        #=\u003e Fear.left(12)\nFear.left(12).select_or_else(-\u003e { -1 }, \u0026:even?) #=\u003e Fear.left(12)\n```\n\n#### Either#select\n\nReturns `Left` of value if the given predicate does not hold for the right value, otherwise, returns `Right`.\n\n```ruby\nFear.right(12).select(\u0026:even?) #=\u003e Fear.right(12)\nFear.right(7).select(\u0026:even?)  #=\u003e Fear.left(7)\nFear.left(12).select(\u0026:even?)  #=\u003e Fear.left(12)\nFear.left(7).select(\u0026:even?)   #=\u003e Fear.left(7)\n```\n\n#### Either#reject\n\nReturns `Left` of value if the given predicate holds for the right value, otherwise, returns `Right`.\n\n```ruby\nFear.right(12).reject(\u0026:even?) #=\u003e Fear.left(12)\nFear.right(7).reject(\u0026:even?)  #=\u003e Fear.right(7)\nFear.left(12).reject(\u0026:even?)  #=\u003e Fear.left(12)\nFear.left(7).reject(\u0026:even?)   #=\u003e Fear.left(7)\n```\n\n#### Either#swap\n\nIf this is a `Left`, then return the left value in `Right` or vice versa.\n\n```ruby\nFear.left('left').swap   #=\u003e Fear.right('left')\nFear.right('right').swap #=\u003e Fear.left('left')\n```\n\n#### Either#left\n\nProjects this `Fear::Either` as a `Fear::Left`.\nThis allows performing right-biased operation of the left\nside of the `Fear::Either`.\n\n```ruby\nFear.left(42).left.map(\u0026:succ)  #=\u003e Fear.left(43)\nFear.right(42).left.map(\u0026:succ) #=\u003e Fear.left(42)\n\nFear.left(42).left.select(\u0026:even?)  #=\u003e Fear.left(42)\nFear.right(42).left.select(\u0026:odd?) #=\u003e Fear.right(42)\n```\n\n#### Either#reduce\n\nApplies `reduce_left` if this is a `Left` or `reduce_right` if this is a `Right`.\n\n```ruby\nresult = possibly_failing_operation()\nlog(\n  result.reduce(\n    -\u003e(ex) { \"Operation failed with #{ex}\" },\n    -\u003e(v) { \"Operation produced value: #{v}\" },\n  )\n)\n```\n\n#### Either#join_right\n\nJoins an `Either` through `Right`. This method requires that the right side of this `Either` is itself an\n`Either` type. This method, and `join_left`, are analogous to `Option#flatten`\n\n```ruby\nFear.right(Fear.right(12)).join_right      #=\u003e Fear.right(12)\nFear.right(Fear.left(\"flower\")).join_right #=\u003e Fear.left(\"flower\")\nFear.left(\"flower\").join_right        #=\u003e Fear.left(\"flower\")\nFear.left(Fear.right(\"flower\")).join_right #=\u003e Fear.left(Fear.right(\"flower\"))\n```\n\n#### Either#join_left\n\nJoins an `Either` through `Left`. This method requires that the left side of this `Either` is itself an\n`Either` type. This method, and `join_right`, are analogous to `Option#flatten`\n\n```ruby\nFear.left(Fear.right(\"flower\")).join_left #=\u003e Fear.right(\"flower\")\nFear.left(Fear.left(12)).join_left        #=\u003e Fear.left(12)\nFear.right(\"daisy\").join_left        #=\u003e Fear.right(\"daisy\")\nFear.right(Fear.left(\"daisy\")).join_left  #=\u003e Fear.right(Fear.left(\"daisy\"))\n```\n\n### Future ([API Documentation](https://www.rubydoc.info/github/bolshakov/fear/master/Fear/Future))\n\nAsynchronous computations that yield futures are created\nwith the `Fear.future` call\n\n```ruby\nsuccess = \"Hello\"\nf = Fear.future { success + ' future!' }\nf.on_success do |result|\n  puts result\nend\n```\n\nMultiple callbacks may be registered; there is no guarantee\nthat they will be executed in a particular order.\n\nThe future may contain an exception and this means\nthat the future failed. Futures obtained through combinators\nhave the same error as the future they were obtained from.\n\n```ruby \nf = Fear.future { 5 }\ng = Fear.future { 3 }\n\nf.flat_map do |x|\n  g.map { |y| x + y }\nend\n```\n\nFutures use [Concurrent::Promise](https://ruby-concurrency.github.io/concurrent-ruby/1.1.5/Concurrent/Promise.html#constructor_details)\nunder the hood. `Fear.future` accepts optional configuration Hash passed directly to underlying promise. For example, \nrun it on custom thread pool.\n\n```ruby\nrequire 'open-uri'\npool = Concurrent::FixedThreadPool.new(5)\nfuture = Fear.future(executor: pool) { open('https://example.com/') }\nfuture.map(\u0026:read).each do |body| \n  puts \"#{body}\"\nend\n\n``` \n\nFutures support common monadic operations -- `#map`, `#flat_map`, and `#each`. That's why it's possible to combine them \nusing `Fear.for`, It returns the Future containing Success of `5 + 3` eventually.\n\n```ruby \nf = Fear.future { 5 }\ng = Fear.future { 3 }\n\nFear.for(f, g) do |x, y|\n  x + y\nend \n``` \n\nFuture goes with the number of callbacks. You can register several callbacks, but the order of execution isn't guaranteed \n\n```ruby\nf = Fear.future { ... } #  call external service\nf.on_success do |result|\n  # handle service response\nend\n\nf.on_failure do |error|\n  # handle exception\nend\n```\n\nor you can wait for Future completion\n\n```ruby \nf.on_complete do |result|\n  result.match do |m|\n    m.success { |value| ... }\n    m.failure { |error| ... }\n  end\nend \n```\n\nIn sake of convenience `#on_success` callback aliased as `#each`.\n\nIt's possible to get future value directly, but since it may be incomplete, `#value` method returns `Fear::Option`. So, \nthere are three possible responses:\n\n```ruby \nfuture.value #=\u003e\n# Fear::Some\u003cFear::Success\u003e #=\u003e future completed with value\n# Fear::Some\u003cFear::Failure\u003e #=\u003e future completed with error\n# Fear::None #=\u003e future not yet completed\n```    \n\nThere is a variety of methods to manipulate with futures. \n\n```ruby\nFear.future { open('http://example.com').read }\n  .transform(\n     -\u003e(value) { ... },\n     -\u003e(error) { ... },\n  )\n\nfuture = Fear.future { 5 }\nfuture.select(\u0026:odd?) # evaluates to Fear.success(5)\nfuture.select(\u0026:even?) # evaluates to Fear.error(NoSuchElementError)\n```\n\nYou can zip several asynchronous computations into one future. For you can call two external services and \nthen zip the results into one future containing array of both responses:  \n\n```ruby \nfuture1 = Fear.future { call_service1 }\nfuture1 = Fear.future { call_service2 }\nfuture1.zip(future2)\n``` \n\nIt  returns the same result as `Fear.future { [call_service1, call_service2] }`,  but the first version performs\ntwo simultaneous calls.\n\nThere are two ways to recover from failure. `Future#recover` is live `#map` for failures:\n\n```ruby\nFear.future { 2 / 0 }.recover do |m|\n  m.case(ZeroDivisionError) { 0 }\nend #=\u003e returns new future of Fear.success(0)\n```\n\nIf the future resolved to success or recovery matcher did not matched, it returns the future `Fear::Failure`.\n\nThe second option is `Future#fallback_to` method. It allows to fallback to result of another future in case of failure\n\n```ruby\nfuture = Fear.future { fail 'error' }\nfallback = Fear.future { 5 }\nfuture.fallback_to(fallback) # evaluates to 5\n```\n\nYou can run callbacks in specific order using `#and_then` method:\n\n```ruby \nf = Fear.future { 5 }\nf.and_then do\n  fail 'runtime error'\nend.and_then do |m|\n  m.success { |value| puts value } # it evaluates this branch\n  m.failure { |error| puts error.massage }\nend\n```\n\n#### Testing future values\n\nSometimes it may be helpful to await for future completion. You can await either future,\nor result. Don't forget to pass timeout in seconds:\n\n\n```ruby \nfuture = Fear.future { 42 }\n\nFear::Await.result(future, 3) #=\u003e 42\n\nFear::Await.ready(future, 3) #=\u003e Fear::Future.successful(42)\n```\n\n### For composition ([API Documentation](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/ForApi))\n\nProvides syntactic sugar for composition of multiple monadic operations. \nIt supports two such operations - `flat_map` and `map`. Any class providing them\nis supported by `For`.\n\n```ruby\nFear.for(Fear.some(2), Fear.some(3)) do |a, b|\n  a * b\nend #=\u003e Fear.some(6)\n```\n\nIf one of operands is None, the result is None\n\n```ruby\nFear.for(Fear.some(2), None) do |a, b| \n  a * b \nend #=\u003e None\n\nFear.for(None, Fear.some(2)) do |a, b| \n  a * b \nend #=\u003e None\n```\n\nLets look at first example:\n\n```ruby\nFear.for(Fear.some(2), None) do |a, b| \n  a * b \nend #=\u003e None\n```\n\nit is translated to:\n\n```ruby\nFear.some(2).flat_map do |a|\n  Fear.some(3).map do |b|\n    a * b\n  end\nend\n```\n\nIt works with arrays as well\n\n```ruby\nFear.for([1, 2], [2, 3], [3, 4]) { |a, b, c| a * b * c }\n  #=\u003e [6, 8, 9, 12, 12, 16, 18, 24]\n\n```\n\nit is translated to:\n\n```ruby\n[1, 2].flat_map do |a|\n  [2, 3].flat_map do |b|\n    [3, 4].map do |c|\n      a * b * c\n    end\n  end\nend\n```\n\nIf you pass lambda as a variable value, it would be evaluated\nonly on demand.\n\n```ruby\nFear.for(proc { None }, proc { raise 'kaboom' } ) do |a, b|\n  a * b\nend #=\u003e None\n```\n\nIt does not fail since `b` is not evaluated.\nYou can refer to previously defined variables from within lambdas.\n\n```ruby\nmaybe_user = find_user('Paul') #=\u003e \u003c#Option value=\u003c#User ...\u003e\u003e\n\nFear.for(maybe_user, -\u003e(user) { user.birthday }) do |user, birthday|\n  \"#{user.name} was born on #{birthday}\"\nend #=\u003e Fear.some('Paul was born on 1987-06-17')\n```\n\n### Pattern Matching ([API Documentation](https://www.rubydoc.info/github/bolshakov/fear/master/Fear/PatternMatchingApi))\n\n#### Syntax\n\nTo pattern match against a value, use `Fear.match` function, and provide at least one case clause:\n\n```ruby \nx = Random.rand(10)\n\nFear.match(x) do |m|\n  m.case(0) { 'zero' }\n  m.case(1) { 'one' }\n  m.case(2) { 'two' }\n  m.else { 'many' }\nend\n```\n\nThe `x` above is a random integer from 0 to 10. The last clause `else` is a “catch all” case \nfor anything other than `0`, `1`, and `2`. If you want to ensure that an Integer value is passed,\nmatching against type available:\n\n```ruby \nFear.match(x) do |m|\n  m.case(Integer, 0) { 'zero' }\n  m.case(Integer, 1) { 'one' }\n  m.case(Integer, 2) { 'two' }\n  m.case(Integer) { 'many' }\nend\n```\n\nProviding  something other than Integer will raise `Fear::MatchError` error.\n\n#### Pattern guards \n\nYou can use whatever you want as a pattern guard, if it respond to `#===` method to to make cases more specific. \n\n```ruby\nm.case(20..40) { |m| \"#{m} is within range\" }\nm.case(-\u003e(x) { x \u003e 10}) { |m| \"#{m} is greater than 10\" } \nm.case(:even?.to_proc) { |x| \"#{x} is even\" }\nm.case(:odd?.to_proc) { |x| \"#{x} is odd\" }\n```\n\nIt's also possible to create matcher and use it several times:\n\n```ruby\nmatcher = Fear.matcher do |m|\n  m.case(Integer) { |n| \"#{n} is a number\" }\n  m.case(String) { |n| \"#{n} is a string\" }\n  m.else  { |n| \"#{n} is a #{n.class}\" }\nend \n\nmatcher.(42) #=\u003e \"42 is a number\"\nmatcher.(10..20) #=\u003e \"10..20 is a Range\"\n``` \n\n#### How to debug pattern extractors?\n\nYou can build pattern manually and ask for failure reason:\n\n```ruby\nFear['Some([:err, 444])'].failure_reason(Fear.some([:err, 445]))\n# =\u003e\nExpected `445` to match:\nSome([:err, 444])\n~~~~~~~~~~~~^\n```\n\nby the way you can also match against such pattern\n\n```ruby\nFear['Some([:err, 444])'] === Fear.some([:err, 445]) #=\u003e false\nFear['Some([:err, 444])'] === Fear.some([:err, 445]) #=\u003e true\n```\n\n#### More examples\n\nFactorial using pattern matching\n\n```ruby\nfactorial = Fear.matcher do |m|\n  m.case(-\u003e(n) { n \u003c= 1} ) { 1 }\n  m.else { |n| n * factorial.(n - 1) }\nend\n\nfactorial.(10) #=\u003e 3628800\n```\n\nFibonacci number\n\n```ruby\nfibonacci = Fear.matcher do |m|\n  m.case(0) { 0 }\n  m.case(1) { 1 }\n  m.case(-\u003e(n) { n \u003e 1}) { |n| fibonacci.(n - 1) + fibonacci.(n - 2) }\nend\n\nfibonacci.(10) #=\u003e 55\n```\n\nBinary tree set implemented using pattern matching https://gist.github.com/bolshakov/3c51bbf7be95066d55d6d1ac8c605a1d\n\n#### Monads pattern matching \n\nYou can use `Option#match`, `Either#match`, and `Try#match` method. It performs matching not \nonly on container itself, but on enclosed value as well. \n\nPattern match against an `Option`\n\n```ruby\nFear.some(42).match do |m|\n  m.some { |x| x * 2 }\n  m.none { 'none' }\nend #=\u003e 84\n```\n\npattern match on enclosed value\n\n```ruby\nFear.some(41).match do |m|\n  m.some(:even?.to_proc) { |x| x / 2 }\n  m.some(:odd?.to_proc, -\u003e(v) { v \u003e 0 }) { |x| x * 2 }\n  m.none { 'none' }\nend #=\u003e 82\n``` \n\nit raises `Fear::MatchError` error if nothing matched. To avoid exception, you can pass `#else` branch\n\n```ruby\nFear.some(42).match do |m|\n  m.some(:odd?.to_proc) { |x| x * 2 }\n  m.else { 'nothing' }\nend #=\u003e nothing\n```\n\nPattern matching works the similar way for `Either` and `Try` monads.\n\nIn sake of performance, you may want to generate pattern matching function and reuse it multiple times:\n\n```ruby\nmatcher = Fear::Option.matcher do |m|\n  m.some(42) { 'Yep' }\n  m.some { 'Nope' }\n  m.none { 'Error' } \nend\n\nmatcher.(Fear.some(42)) #=\u003e 'Yep'\nmatcher.(Fear.some(40)) #=\u003e 'Nope'\n``` \n\n#### Under the hood \n\nPattern matcher is a combination of partial functions wrapped into nice DSL. Every partial function \ndefined on domain described with a guard.\n\n```ruby\npf = Fear.case(Integer) { |x| x / 2 }\npf.defined_at?(4) #=\u003e true\npf.defined_at?('Foo') #=\u003e false\npf.call('Foo') #=\u003e raises Fear::MatchError\npf.call_or_else('Foo') { 'not a number' } #=\u003e 'not a number'\npf.call_or_else(4) { 'not a number' } #=\u003e 2\npf.lift.call('Foo') #=\u003e Fear::None\npf.lift.call(4) #=\u003e Fear.some(2)\n```\n\nIt uses `#===` method under the hood, so you can pass:\n\n* Class to check kind of an object.\n* Lambda to evaluate it against an object.\n* Any literal, like `4`, `\"Foobar\"`, `:not_found` etc.\n* Qo matcher -- `m.case(Qo[name: 'John']) { .... }`\n \nPartial functions may be combined with each other:\n\n```ruby\nis_even = Fear.case(-\u003e(arg) { arg % 2 == 0}) { |arg| \"#{arg} is even\" }\nis_odd = Fear.case(-\u003e(arg) { arg % 2 == 1}) { |arg| \"#{arg} is odd\" }\n\n(10..20).map(\u0026is_even.or_else(is_odd))\n\nto_integer = Fear.case(String, \u0026:to_i)\ninteger_two_times = Fear.case(Integer) { |x| x * 2 }\n\ntwo_times = to_integer.and_then(integer_two_times).or_else(integer_two_times)\ntwo_times.(4) #=\u003e 8\ntwo_times.('42') #=\u003e 84\n```\n\nSince matcher is just a syntactic sugar for partial functions, you can combine matchers with partial\nfunctions and each other. \n\n```ruby\nhandle_numbers = Fear.case(Integer, \u0026:itself).and_then(\n  Fear.matcher do |m|\n    m.case(0) { 'zero' }\n    m.case(-\u003e(n) { n \u003c 10 }) { 'smaller than ten' }  \n    m.case(-\u003e(n) { n \u003e 10 }) { 'bigger than ten' }\n  end\n)\n\nhandle_strings = Fear.case(String, \u0026:itself).and_then(\n  Fear.matcher do |m|\n    m.case('zero') { 0 }\n    m.case('one') { 1 }\n    m.else { 'unexpected' }\n  end\n)\n\nhandle = handle_numbers.or_else(handle_strings)\nhandle.(0) #=\u003e 'zero'\nhandle.(12) #=\u003e 'bigger than ten'\nhandle.('one') #=\u003e 1\n```\n\n### Native pattern-matching \n\nStarting from ruby 2.7 you can use native pattern matching capabilities:\n\n```ruby \ncase Fear.some(42)\nin Fear::Some(x)\n  x * 2\nin Fear::None \n  'none'\nend #=\u003e 84\n\ncase Fear.some(41)\nin Fear::Some(x) if x.even?\n  x / 2\nin Fear::Some(x) if x.odd? \u0026\u0026 x \u003e 0\n  x * 2\nin Fear::None\n  'none'\nend #=\u003e 82\n\ncase Fear.some(42)\nin Fear::Some(x) if x.odd?\n  x * 2 \nelse \n  'nothing'\nend #=\u003e nothing\n```\n\nIt's possible to pattern match against Fear::Either and Fear::Try as well:\n\n```ruby \ncase either \nin Fear::Right(Integer | String =\u003e x)\n  \"integer or string: #{x}\"\nin Fear::Left(String =\u003e error_code) if error_code = :not_found \n  'not found'\nend  \n```\n\n```ruby \ncase Fear.try { 10 / x } \nin Fear::Failure(ZeroDivisionError)\n  # ..\nin Fear::Success(x) \n  # ..\nend  \n```\n\n### Dry-Types integration\n\nTo use `Fear::Option` as optional type for `Dry::Types` use the [dry-types-fear] gem. \n\n## Testing\n\nTo simplify testing, you may use [fear-rspec](https://github.com/bolshakov/fear-rspec) gem. It\nprovides a bunch of rspec matchers.\n\n## Contributing\n\n1. Fork it ( https://github.com/bolshakov/fear/fork )\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n## Alternatives\n\n* [algebrick](https://github.com/pitr-ch/algebrick)\n* [deterministic](https://github.com/pzol/deterministic)\n* [dry-monads](https://github.com/dry-rb/dry-monads)\n* [kleisli](https://github.com/txus/kleisli)\n* [maybe](https://github.com/bhb/maybe)\n* [ruby-possibly](https://github.com/rap1ds/ruby-possibly)\n* [rumonade](https://github.com/ms-ati/rumonade)\n\n\n[dry-types-fear]: https://github.com/bolshakov/dry-types-fear\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbolshakov%2Ffear","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbolshakov%2Ffear","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbolshakov%2Ffear/lists"}