lib/qo/public_api.rb in qo-0.5.0 vs lib/qo/public_api.rb in qo-0.99.0

- old
+ new

@@ -71,19 +71,23 @@ # m.when(:even?) { |v| v * 3 } # m.else { |v| v - 1 } # }) # => [0, 6, 2] # + # @param destructure: [Boolean] + # Whether or not to destructure an object before yielding it to the + # given function. + # # @param fn [Proc] # Body of the matcher, as shown in examples # # @return [Qo::PatternMatch] # A function awaiting a value to match against - def match(&fn) + def match(destructure: false, &fn) return proc { false } unless block_given? - Qo::Matchers::PatternMatch.new(&fn) + Qo::PatternMatchers::PatternMatch.new(destructure: destructure, &fn) end # Similar to the common case statement of Ruby, except in that it behaves # as if `Array#===` and `Hash#===` exist in the form of Qo matchers. # @@ -103,21 +107,141 @@ # => 2 # # @param value [Any] # Value to match against # + # @param destructure: [Boolean] + # Whether or not to destructure an object before yielding it to the + # given function. + # # @param &fn [Proc] # Body of the matcher, as shown above # # @return [Any] # The result of calling a pattern match with a provided value - def case(value, &fn) - Qo::Matchers::PatternMatch.new(&fn).call(value) + def case(value, destructure: false, &fn) + Qo::PatternMatchers::PatternMatch.new(destructure: destructure, &fn).call(value) end - # Abstraction for creating a matcher, allowing for common error handling scenarios. + # Similar to `match`, except it uses a `ResultPatternMatch` which instead + # responds to tuple types: # + # @example + # + # ```ruby + # pm = Qo.result_match { |m| + # m.success { |v| v + 10 } + # m.failure { |v| "Error: #{v}" } + # } + # + # pm.call([:ok, 3]) + # # => 13 + # + # pm.call([:err, "No Good"]) + # # => "Error: No Good" + # ``` + # + # @param destructure: [Boolean] + # Whether or not to destructure an object before yielding it to the + # given function. + # + # @param &fn [Proc] + # Body of the matcher, as shown above + # + # @see match + # + # @return [Proc[Any] => Any] + # Proc awaiting a value to match against. + def result_match(destructure: false, &fn) + return proc { false } unless block_given? + + Qo::PatternMatchers::ResultPatternMatch.new(destructure: destructure, &fn) + end + + # Similar to `case`, except it uses a `ResultPatternMatch` instead. + # + # @see match + # @see result_match + # @see case + # + # @param target [Any] + # Target to match against + # + # @param destructure: [Boolean] + # Whether or not to destructure an object before yielding it to the + # given function. + # + # @param &fn [Proc] + # Body of the matcher, as shown above + # + # @return [Any] + # Result of the match + def result_case(target, destructure: false, &fn) + Qo::PatternMatchers::ResultPatternMatch + .new(destructure: destructure, &fn) + .call(target) + end + + # Dynamically creates a new branch to be used with custom pattern matchers. + # + # @param name: [String] + # Name of the branch. This is what binds to the pattern match as a method, + # meaning a name of `where` will result in calling it as `m.where`. + # + # @param precondition: Any [Symbol, #===] + # A precondition to the branch being considered true. This is done for + # static conditions like a certain type or perhaps checking a tuple type + # like `[:ok, value]`. + # + # If a `Symbol` is given, Qo will coerce it into a proc. This is done to + # make a nicer shorthand for creating a branch. + # + # @param extractor: IDENTITY [Proc, Symbol] + # How to pull the value out of a target object when a branch matches before + # calling the associated function. For a monadic type this might be something + # like extracting the value before yielding to the given block. + # + # If a `Symbol` is given, Qo will coerce it into a proc. This is done to + # make a nicer shorthand for creating a branch. + # + # @param destructure: false + # Whether or not to destructure the given object before yielding to the + # associated block. This means that the given block now places great + # importance on the argument names, as they'll be used to extract values + # from the associated object by that same method name, or key name in the + # case of hashes. + # + # @param default: false [Boolean] + # Whether this branch is considered to be a default condition. This is + # done to ensure that a branch runs last after all other conditions have + # failed. An example of this would be an `else` branch. + # + # @return [Class] + # Anonymous branch class to be bound to a constant or used directly + def create_branch(name:, precondition: Any, extractor: IDENTITY, destructure: false, default: false) + Qo::Branches::Branch.create( + name: name, + precondition: precondition, + extractor: extractor, + destructure: destructure, + default: default + ) + end + + # Creates a new type of pattern matcher from a set of branches + # + # @param branches: [Array[Branch]] + # An array of branches that this pattern matcher will respond to + # + # @return [Class] + # Anonymous pattern matcher to be bound to a constant or used directly + def create_pattern_match(branches:) + Qo::PatternMatchers::PatternMatch.create(branches: branches) + end + + # Abstraction for creating a matcher. + # # @param type [String] # Type of matcher # # @param array_matchers [Array[Any]] # Array-like conditionals @@ -125,11 +249,9 @@ # @param keyword_matchers [Hash[Any, Any]] # Keyword style conditionals # # @return [Qo::Matcher] private def create_matcher(type, array_matchers, keyword_matchers) - raise MultipleMatchersProvided if !array_matchers.empty? && !keyword_matchers.empty? - - Qo::Matchers::BaseMatcher.new(type, array_matchers, keyword_matchers) + Qo::Matchers::Matcher.new(type, array_matchers, keyword_matchers) end end end