# frozen_string_literal: true RSpec.describe Fear::PartialFunction, "#and_then" do context "proc" do subject(:pf_and_f) { partial_function.and_then(&function) } let(:partial_function) { Fear.case(->(x) { x.even? }) { |x| "pf: #{x}" } } let(:function) { ->(x) { "f: #{x}" } } describe "#defined_at?" do context "defined" do subject { pf_and_f.defined_at?(4) } it { is_expected.to eq(true) } end context "not defined" do subject { pf_and_f.defined_at?(3) } it { is_expected.to eq(false) } end end describe "#call" do context "defined" do subject { pf_and_f.(4) } it { is_expected.to eq("f: pf: 4") } end context "not defined" do subject { -> { pf_and_f.(3) } } it { is_expected.to raise_error(Fear::MatchError, "partial function not defined at: 3") } end end describe "#call_or_else" do let(:fallback) { ->(x) { "fallback: #{x}" } } context "defined" do subject { pf_and_f.call_or_else(4, &fallback) } it { is_expected.to eq("f: pf: 4") } end context "not defined" do subject { pf_and_f.call_or_else(3, &fallback) } it { is_expected.to eq("fallback: 3") } end end end context "partial function" do subject(:first_and_then_second) { first.and_then(second) } describe "#defined_at?" do context "first defined, second defined on result of first" do subject { first_and_then_second.defined_at?(6) } let(:first) { Fear.case(->(x) { x.even? }) { |x| x / 2 } } let(:second) { Fear.case(->(x) { x % 3 == 0 }) { |x| x / 3 } } it { is_expected.to eq(true) } end context "first defined, second not defined on result of first" do subject { first_and_then_second.defined_at?(4) } let(:first) { Fear.case(->(x) { x.even? }) { |x| x / 2 } } let(:second) { Fear.case(->(x) { x % 3 == 0 }) { |x| x / 3 } } it { is_expected.to eq(false) } end context "first not defined" do subject { first_and_then_second.defined_at?(3) } let(:first) { Fear.case(->(x) { x.even? }) { |x| "first: #{x}" } } let(:second) { Fear.case(->(x) { x % 3 == 0 }) { |x| "second: #{x}" } } it { is_expected.to eq(false) } end end describe "#call" do context "first defined, second defined on result of first" do subject { first_and_then_second.(6) } let(:first) { Fear.case(->(x) { x.even? }) { |x| x / 2 } } let(:second) { Fear.case(->(x) { x % 3 == 0 }) { |x| x / 3 } } it { is_expected.to eq(1) } end context "first defined, second not defined on result of first" do subject { -> { first_and_then_second.(4) } } let(:first) { Fear.case(->(x) { x.even? }) { |x| x / 2 } } let(:second) { Fear.case(->(x) { x % 3 == 0 }) { |x| x / 3 } } it { is_expected.to raise_error(Fear::MatchError, "partial function not defined at: 2") } end context "first not defined" do subject { -> { first_and_then_second.(3) } } let(:first) { Fear.case(->(x) { x.even? }) { |x| "first: #{x}" } } let(:second) { Fear.case(->(x) { x % 3 == 0 }) { |x| "second: #{x}" } } it { is_expected.to raise_error(Fear::MatchError, "partial function not defined at: 3") } end end describe "#call_or_else" do let(:fallback) { ->(x) { "fallback: #{x}" } } context "first defined, second defined on result of first" do subject { first_and_then_second.call_or_else(6, &fallback) } let(:first) { Fear.case(->(x) { x.even? }) { |x| x / 2 } } let(:second) { Fear.case(->(x) { x % 3 == 0 }) { |x| x / 3 } } it { is_expected.to eq(1) } end context "first defined, second not defined on result of first" do subject { first_and_then_second.call_or_else(4, &fallback) } let(:first) { Fear.case(->(x) { x.even? }) { |x| x / 2 } } let(:second) { Fear.case(->(x) { x % 3 == 0 }) { |x| x / 3 } } it { is_expected.to eq("fallback: 4") } end context "first not defined" do subject { first_and_then_second.call_or_else(3, &fallback) } let(:first) { Fear.case(->(x) { x.even? }) { |x| "first: #{x}" } } let(:second) { Fear.case { |x| "second: #{x}" } } it { is_expected.to eq("fallback: 3") } end end end end