# <!-- rdoc-file=proc.c -->
# A `Proc` object is an encapsulation of a block of code, which can be stored in
# a local variable, passed to a method or another Proc, and can be called. Proc
# is an essential concept in Ruby and a core of its functional programming
# features.
#
#     square = Proc.new {|x| x**2 }
#
#     square.call(3)  #=> 9
#     # shorthands:
#     square.(3)      #=> 9
#     square[3]       #=> 9
#
# Proc objects are *closures*, meaning they remember and can use the entire
# context in which they were created.
#
#     def gen_times(factor)
#       Proc.new {|n| n*factor } # remembers the value of factor at the moment of creation
#     end
#
#     times3 = gen_times(3)
#     times5 = gen_times(5)
#
#     times3.call(12)               #=> 36
#     times5.call(5)                #=> 25
#     times3.call(times5.call(4))   #=> 60
#
# ## Creation
#
# There are several methods to create a Proc
#
# *   Use the Proc class constructor:
#
#         proc1 = Proc.new {|x| x**2 }
#
# *   Use the Kernel#proc method as a shorthand of Proc.new:
#
#         proc2 = proc {|x| x**2 }
#
# *   Receiving a block of code into proc argument (note the `&`):
#
#         def make_proc(&block)
#           block
#         end
#
#         proc3 = make_proc {|x| x**2 }
#
# *   Construct a proc with lambda semantics using the Kernel#lambda method (see
#     below for explanations about lambdas):
#
#         lambda1 = lambda {|x| x**2 }
#
# *   Use the [Lambda proc
#     literal](rdoc-ref:syntax/literals.rdoc@Lambda+Proc+Literals) syntax (also
#     constructs a proc with lambda semantics):
#
#         lambda2 = ->(x) { x**2 }
#
# ## Lambda and non-lambda semantics
#
# Procs are coming in two flavors: lambda and non-lambda (regular procs).
# Differences are:
#
# *   In lambdas, `return` and `break` means exit from this lambda;
# *   In non-lambda procs, `return` means exit from embracing method (and will
#     throw `LocalJumpError` if invoked outside the method);
# *   In non-lambda procs, `break` means exit from the method which the block
#     given for. (and will throw `LocalJumpError` if invoked after the method
#     returns);
# *   In lambdas, arguments are treated in the same way as in methods: strict,
#     with `ArgumentError` for mismatching argument number, and no additional
#     argument processing;
# *   Regular procs accept arguments more generously: missing arguments are
#     filled with `nil`, single Array arguments are deconstructed if the proc
#     has multiple arguments, and there is no error raised on extra arguments.
#
# Examples:
#
#     # +return+ in non-lambda proc, +b+, exits +m2+.
#     # (The block +{ return }+ is given for +m1+ and embraced by +m2+.)
#     $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1 { return }; $a << :m2 end; m2; p $a
#     #=> []
#
#     # +break+ in non-lambda proc, +b+, exits +m1+.
#     # (The block +{ break }+ is given for +m1+ and embraced by +m2+.)
#     $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1 { break }; $a << :m2 end; m2; p $a
#     #=> [:m2]
#
#     # +next+ in non-lambda proc, +b+, exits the block.
#     # (The block +{ next }+ is given for +m1+ and embraced by +m2+.)
#     $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1 { next }; $a << :m2 end; m2; p $a
#     #=> [:m1, :m2]
#
#     # Using +proc+ method changes the behavior as follows because
#     # The block is given for +proc+ method and embraced by +m2+.
#     $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&proc { return }); $a << :m2 end; m2; p $a
#     #=> []
#     $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&proc { break }); $a << :m2 end; m2; p $a
#     # break from proc-closure (LocalJumpError)
#     $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&proc { next }); $a << :m2 end; m2; p $a
#     #=> [:m1, :m2]
#
#     # +return+, +break+ and +next+ in the stubby lambda exits the block.
#     # (+lambda+ method behaves same.)
#     # (The block is given for stubby lambda syntax and embraced by +m2+.)
#     $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&-> { return }); $a << :m2 end; m2; p $a
#     #=> [:m1, :m2]
#     $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&-> { break }); $a << :m2 end; m2; p $a
#     #=> [:m1, :m2]
#     $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&-> { next }); $a << :m2 end; m2; p $a
#     #=> [:m1, :m2]
#
#     p = proc {|x, y| "x=#{x}, y=#{y}" }
#     p.call(1, 2)      #=> "x=1, y=2"
#     p.call([1, 2])    #=> "x=1, y=2", array deconstructed
#     p.call(1, 2, 8)   #=> "x=1, y=2", extra argument discarded
#     p.call(1)         #=> "x=1, y=", nil substituted instead of error
#
#     l = lambda {|x, y| "x=#{x}, y=#{y}" }
#     l.call(1, 2)      #=> "x=1, y=2"
#     l.call([1, 2])    # ArgumentError: wrong number of arguments (given 1, expected 2)
#     l.call(1, 2, 8)   # ArgumentError: wrong number of arguments (given 3, expected 2)
#     l.call(1)         # ArgumentError: wrong number of arguments (given 1, expected 2)
#
#     def test_return
#       -> { return 3 }.call      # just returns from lambda into method body
#       proc { return 4 }.call    # returns from method
#       return 5
#     end
#
#     test_return # => 4, return from proc
#
# Lambdas are useful as self-sufficient functions, in particular useful as
# arguments to higher-order functions, behaving exactly like Ruby methods.
#
# Procs are useful for implementing iterators:
#
#     def test
#       [[1, 2], [3, 4], [5, 6]].map {|a, b| return a if a + b > 10 }
#                                 #  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#     end
#
# Inside `map`, the block of code is treated as a regular (non-lambda) proc,
# which means that the internal arrays will be deconstructed to pairs of
# arguments, and `return` will exit from the method `test`. That would not be
# possible with a stricter lambda.
#
# You can tell a lambda from a regular proc by using the #lambda? instance
# method.
#
# Lambda semantics is typically preserved during the proc lifetime, including
# `&`-deconstruction to a block of code:
#
#     p = proc {|x, y| x }
#     l = lambda {|x, y| x }
#     [[1, 2], [3, 4]].map(&p) #=> [1, 3]
#     [[1, 2], [3, 4]].map(&l) # ArgumentError: wrong number of arguments (given 1, expected 2)
#
# The only exception is dynamic method definition: even if defined by passing a
# non-lambda proc, methods still have normal semantics of argument checking.
#
#     class C
#       define_method(:e, &proc {})
#     end
#     C.new.e(1,2)       #=> ArgumentError
#     C.new.method(:e).to_proc.lambda?   #=> true
#
# This exception ensures that methods never have unusual argument passing
# conventions, and makes it easy to have wrappers defining methods that behave
# as usual.
#
#     class C
#       def self.def2(name, &body)
#         define_method(name, &body)
#       end
#
#       def2(:f) {}
#     end
#     C.new.f(1,2)       #=> ArgumentError
#
# The wrapper `def2` receives *body* as a non-lambda proc, yet defines a method
# which has normal semantics.
#
# ## Conversion of other objects to procs
#
# Any object that implements the `to_proc` method can be converted into a proc
# by the `&` operator, and therefore can be consumed by iterators.
#
#     class Greeter
#       def initialize(greeting)
#         @greeting = greeting
#       end
#
#       def to_proc
#         proc {|name| "#{@greeting}, #{name}!" }
#       end
#     end
#
#     hi = Greeter.new("Hi")
#     hey = Greeter.new("Hey")
#     ["Bob", "Jane"].map(&hi)    #=> ["Hi, Bob!", "Hi, Jane!"]
#     ["Bob", "Jane"].map(&hey)   #=> ["Hey, Bob!", "Hey, Jane!"]
#
# Of the Ruby core classes, this method is implemented by Symbol, Method, and
# Hash.
#
#     :to_s.to_proc.call(1)           #=> "1"
#     [1, 2].map(&:to_s)              #=> ["1", "2"]
#
#     method(:puts).to_proc.call(1)   # prints 1
#     [1, 2].each(&method(:puts))     # prints 1, 2
#
#     {test: 1}.to_proc.call(:test)       #=> 1
#     %i[test many keys].map(&{test: 1})  #=> [1, nil, nil]
#
# ## Orphaned Proc
#
# `return` and `break` in a block exit a method. If a Proc object is generated
# from the block and the Proc object survives until the method is returned,
# `return` and `break` cannot work. In such case, `return` and `break` raises
# LocalJumpError. A Proc object in such situation is called as orphaned Proc
# object.
#
# Note that the method to exit is different for `return` and `break`. There is a
# situation that orphaned for `break` but not orphaned for `return`.
#
#     def m1(&b) b.call end; def m2(); m1 { return } end; m2 # ok
#     def m1(&b) b.call end; def m2(); m1 { break } end; m2 # ok
#
#     def m1(&b) b end; def m2(); m1 { return }.call end; m2 # ok
#     def m1(&b) b end; def m2(); m1 { break }.call end; m2 # LocalJumpError
#
#     def m1(&b) b end; def m2(); m1 { return } end; m2.call # LocalJumpError
#     def m1(&b) b end; def m2(); m1 { break } end; m2.call # LocalJumpError
#
# Since `return` and `break` exits the block itself in lambdas, lambdas cannot
# be orphaned.
#
# ## Anonymous block parameters
#
# To simplify writing short blocks, Ruby provides two different types of
# anonymous parameters: `it` (single parameter) and numbered ones: `_1`, `_2`
# and so on.
#
#     # Explicit parameter:
#     %w[test me please].each { |str| puts str.upcase } # prints TEST, ME, PLEASE
#     (1..5).map { |i| i**2 } # => [1, 4, 9, 16, 25]
#
#     # it:
#     %w[test me please].each { puts it.upcase } # prints TEST, ME, PLEASE
#     (1..5).map { it**2 } # => [1, 4, 9, 16, 25]
#
#     # Numbered parameter:
#     %w[test me please].each { puts _1.upcase } # prints TEST, ME, PLEASE
#     (1..5).map { _1**2 } # => [1, 4, 9, 16, 25]
#
# ### `it`
#
# `it` is a name that is available inside a block when no explicit parameters
# defined, as shown above.
#
#     %w[test me please].each { puts it.upcase } # prints TEST, ME, PLEASE
#     (1..5).map { it**2 } # => [1, 4, 9, 16, 25]
#
# `it` is a "soft keyword": it is not a reserved name, and can be used as a name
# for methods and local variables:
#
#     it = 5 # no warnings
#     def it(&block) # RSpec-like API, no warnings
#        # ...
#     end
#
# `it` can be used as a local variable even in blocks that use it as an implicit
# parameter (though this style is obviously confusing):
#
#     [1, 2, 3].each {
#       # takes a value of implicit parameter "it" and uses it to
#       # define a local variable with the same name
#       it = it**2
#       p it
#     }
#
# In a block with explicit parameters defined `it` usage raises an exception:
#
#     [1, 2, 3].each { |x| p it }
#     # syntax error found (SyntaxError)
#     # [1, 2, 3].each { |x| p it }
#     #                        ^~ `it` is not allowed when an ordinary parameter is defined
#
# But if a local name (variable or method) is available, it would be used:
#
#     it = 5
#     [1, 2, 3].each { |x| p it }
#     # Prints 5, 5, 5
#
# Blocks using `it` can be nested:
#
#     %w[test me].each { it.each_char { p it } }
#     # Prints "t", "e", "s", "t", "m", "e"
#
# Blocks using `it` are considered to have one parameter:
#
#     p = proc { it**2 }
#     l = lambda { it**2 }
#     p.parameters     # => [[:opt, nil]]
#     p.arity          # => 1
#     l.parameters     # => [[:req]]
#     l.arity          # => 1
#
# ### Numbered parameters
#
# Numbered parameters are another way to name block parameters implicitly.
# Unlike `it`, numbered parameters allow to refer to several parameters in one
# block.
#
#     %w[test me please].each { puts _1.upcase } # prints TEST, ME, PLEASE
#     {a: 100, b: 200}.map { "#{_1} = #{_2}" } # => "a = 100", "b = 200"
#
# Parameter names from `_1` to `_9` are supported:
#
#     [10, 20, 30].zip([40, 50, 60], [70, 80, 90]).map { _1 + _2 + _3 }
#     # => [120, 150, 180]
#
# Though, it is advised to resort to them wisely, probably limiting yourself to
# `_1` and `_2`, and to one-line blocks.
#
# Numbered parameters can't be used together with explicitly named ones:
#
#     [10, 20, 30].map { |x| _1**2 }
#     # SyntaxError (ordinary parameter is defined)
#
# Numbered parameters can't be mixed with `it` either:
#
#     [10, 20, 30].map { _1 + it }
#     # SyntaxError: `it` is not allowed when a numbered parameter is already used
#
# To avoid conflicts, naming local variables or method arguments `_1`, `_2` and
# so on, causes an error.
#
#       _1 = 'test'
#     # ^~ _1 is reserved for numbered parameters (SyntaxError)
#
# Using implicit numbered parameters affects block's arity:
#
#     p = proc { _1 + _2 }
#     l = lambda { _1 + _2 }
#     p.parameters     # => [[:opt, :_1], [:opt, :_2]]
#     p.arity          # => 2
#     l.parameters     # => [[:req, :_1], [:req, :_2]]
#     l.arity          # => 2
#
# Blocks with numbered parameters can't be nested:
#
#     %w[test me].each { _1.each_char { p _1 } }
#     # numbered parameter is already used in outer block (SyntaxError)
#     # %w[test me].each { _1.each_char { p _1 } }
#     #                    ^~
#
class Proc
  interface _Callable
    def call: (?) -> untyped
  end

  # <!--
  #   rdoc-file=proc.c
  #   - Proc.new {|...| block } -> a_proc
  # -->
  # Creates a new Proc object, bound to the current context.
  #
  #     proc = Proc.new { "hello" }
  #     proc.call   #=> "hello"
  #
  # Raises ArgumentError if called without a block.
  #
  #     Proc.new    #=> ArgumentError
  #
  def self.new: () { (?) -> untyped } -> instance

  def clone: () -> instance
  def dup: () -> instance

  # <!-- rdoc-file=proc.c -->
  # Invokes the block, setting the block's parameters to the values in *params*
  # using something close to method calling semantics. Returns the value of the
  # last expression evaluated in the block.
  #
  #     a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
  #     a_proc.call(9, 1, 2, 3)    #=> [9, 18, 27]
  #     a_proc[9, 1, 2, 3]         #=> [9, 18, 27]
  #     a_proc.(9, 1, 2, 3)        #=> [9, 18, 27]
  #     a_proc.yield(9, 1, 2, 3)   #=> [9, 18, 27]
  #
  # Note that `prc.()` invokes `prc.call()` with the parameters given.  It's
  # syntactic sugar to hide "call".
  #
  # For procs created using #lambda or `->()` an error is generated if the wrong
  # number of parameters are passed to the proc.  For procs created using Proc.new
  # or Kernel.proc, extra parameters are silently discarded and missing parameters
  # are set to `nil`.
  #
  #     a_proc = proc {|a,b| [a,b] }
  #     a_proc.call(1)   #=> [1, nil]
  #
  #     a_proc = lambda {|a,b| [a,b] }
  #     a_proc.call(1)   # ArgumentError: wrong number of arguments (given 1, expected 2)
  #
  # See also Proc#lambda?.
  #
  alias === call

  # <!-- rdoc-file=proc.c -->
  # Invokes the block, setting the block's parameters to the values in *params*
  # using something close to method calling semantics. Returns the value of the
  # last expression evaluated in the block.
  #
  #     a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
  #     a_proc.call(9, 1, 2, 3)    #=> [9, 18, 27]
  #     a_proc[9, 1, 2, 3]         #=> [9, 18, 27]
  #     a_proc.(9, 1, 2, 3)        #=> [9, 18, 27]
  #     a_proc.yield(9, 1, 2, 3)   #=> [9, 18, 27]
  #
  # Note that `prc.()` invokes `prc.call()` with the parameters given.  It's
  # syntactic sugar to hide "call".
  #
  # For procs created using #lambda or `->()` an error is generated if the wrong
  # number of parameters are passed to the proc.  For procs created using Proc.new
  # or Kernel.proc, extra parameters are silently discarded and missing parameters
  # are set to `nil`.
  #
  #     a_proc = proc {|a,b| [a,b] }
  #     a_proc.call(1)   #=> [1, nil]
  #
  #     a_proc = lambda {|a,b| [a,b] }
  #     a_proc.call(1)   # ArgumentError: wrong number of arguments (given 1, expected 2)
  #
  # See also Proc#lambda?.
  #
  alias yield call

  # <!--
  #   rdoc-file=proc.c
  #   - prc << g -> a_proc
  # -->
  # Returns a proc that is the composition of this proc and the given *g*. The
  # returned proc takes a variable number of arguments, calls *g* with them then
  # calls this proc with the result.
  #
  #     f = proc {|x| x * x }
  #     g = proc {|x| x + x }
  #     p (f << g).call(2) #=> 16
  #
  # See Proc#>> for detailed explanations.
  #
  def <<: (_Callable callable) -> Proc

  # <!--
  #   rdoc-file=proc.c
  #   - prc >> g -> a_proc
  # -->
  # Returns a proc that is the composition of this proc and the given *g*. The
  # returned proc takes a variable number of arguments, calls this proc with them
  # then calls *g* with the result.
  #
  #     f = proc {|x| x * x }
  #     g = proc {|x| x + x }
  #     p (f >> g).call(2) #=> 8
  #
  # *g* could be other Proc, or Method, or any other object responding to `call`
  # method:
  #
  #     class Parser
  #       def self.call(text)
  #          # ...some complicated parsing logic...
  #       end
  #     end
  #
  #     pipeline = File.method(:read) >> Parser >> proc { |data| puts "data size: #{data.count}" }
  #     pipeline.call('data.json')
  #
  # See also Method#>> and Method#<<.
  #
  def >>: (_Callable callable) -> Proc

  # <!--
  #   rdoc-file=proc.c
  #   - prc == other -> true or false
  #   - prc.eql?(other) -> true or false
  # -->
  # Two procs are the same if, and only if, they were created from the same code
  # block.
  #
  #     def return_block(&block)
  #       block
  #     end
  #
  #     def pass_block_twice(&block)
  #       [return_block(&block), return_block(&block)]
  #     end
  #
  #     block1, block2 = pass_block_twice { puts 'test' }
  #     # Blocks might be instantiated into Proc's lazily, so they may, or may not,
  #     # be the same object.
  #     # But they are produced from the same code block, so they are equal
  #     block1 == block2
  #     #=> true
  #
  #     # Another Proc will never be equal, even if the code is the "same"
  #     block1 == proc { puts 'test' }
  #     #=> false
  #
  def ==: (untyped other) -> bool

  # <!-- rdoc-file=proc.c -->
  # Two procs are the same if, and only if, they were created from the same code
  # block.
  #
  #     def return_block(&block)
  #       block
  #     end
  #
  #     def pass_block_twice(&block)
  #       [return_block(&block), return_block(&block)]
  #     end
  #
  #     block1, block2 = pass_block_twice { puts 'test' }
  #     # Blocks might be instantiated into Proc's lazily, so they may, or may not,
  #     # be the same object.
  #     # But they are produced from the same code block, so they are equal
  #     block1 == block2
  #     #=> true
  #
  #     # Another Proc will never be equal, even if the code is the "same"
  #     block1 == proc { puts 'test' }
  #     #=> false
  #
  alias eql? ==

  # <!--
  #   rdoc-file=proc.c
  #   - prc.arity -> integer
  # -->
  # Returns the number of mandatory arguments. If the block is declared to take no
  # arguments, returns 0. If the block is known to take exactly n arguments,
  # returns n. If the block has optional arguments, returns -n-1, where n is the
  # number of mandatory arguments, with the exception for blocks that are not
  # lambdas and have only a finite number of optional arguments; in this latter
  # case, returns n. Keyword arguments will be considered as a single additional
  # argument, that argument being mandatory if any keyword argument is mandatory.
  # A #proc with no argument declarations is the same as a block declaring `||` as
  # its arguments.
  #
  #     proc {}.arity                  #=>  0
  #     proc { || }.arity              #=>  0
  #     proc { |a| }.arity             #=>  1
  #     proc { |a, b| }.arity          #=>  2
  #     proc { |a, b, c| }.arity       #=>  3
  #     proc { |*a| }.arity            #=> -1
  #     proc { |a, *b| }.arity         #=> -2
  #     proc { |a, *b, c| }.arity      #=> -3
  #     proc { |x:, y:, z:0| }.arity   #=>  1
  #     proc { |*a, x:, y:0| }.arity   #=> -2
  #
  #     proc   { |a=0| }.arity         #=>  0
  #     lambda { |a=0| }.arity         #=> -1
  #     proc   { |a=0, b| }.arity      #=>  1
  #     lambda { |a=0, b| }.arity      #=> -2
  #     proc   { |a=0, b=0| }.arity    #=>  0
  #     lambda { |a=0, b=0| }.arity    #=> -1
  #     proc   { |a, b=0| }.arity      #=>  1
  #     lambda { |a, b=0| }.arity      #=> -2
  #     proc   { |(a, b), c=0| }.arity #=>  1
  #     lambda { |(a, b), c=0| }.arity #=> -2
  #     proc   { |a, x:0, y:0| }.arity #=>  1
  #     lambda { |a, x:0, y:0| }.arity #=> -2
  #
  def arity: () -> Integer

  # <!--
  #   rdoc-file=proc.c
  #   - prc.binding    -> binding
  # -->
  # Returns the binding associated with *prc*.
  #
  #     def fred(param)
  #       proc {}
  #     end
  #
  #     b = fred(99)
  #     eval("param", b.binding)   #=> 99
  #
  def binding: () -> Binding

  # <!--
  #   rdoc-file=proc.c
  #   - prc.call(params,...)   -> obj
  #   - prc[params,...]        -> obj
  #   - prc.(params,...)       -> obj
  #   - prc.yield(params,...)  -> obj
  # -->
  # Invokes the block, setting the block's parameters to the values in *params*
  # using something close to method calling semantics. Returns the value of the
  # last expression evaluated in the block.
  #
  #     a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
  #     a_proc.call(9, 1, 2, 3)    #=> [9, 18, 27]
  #     a_proc[9, 1, 2, 3]         #=> [9, 18, 27]
  #     a_proc.(9, 1, 2, 3)        #=> [9, 18, 27]
  #     a_proc.yield(9, 1, 2, 3)   #=> [9, 18, 27]
  #
  # Note that `prc.()` invokes `prc.call()` with the parameters given.  It's
  # syntactic sugar to hide "call".
  #
  # For procs created using #lambda or `->()` an error is generated if the wrong
  # number of parameters are passed to the proc.  For procs created using Proc.new
  # or Kernel.proc, extra parameters are silently discarded and missing parameters
  # are set to `nil`.
  #
  #     a_proc = proc {|a,b| [a,b] }
  #     a_proc.call(1)   #=> [1, nil]
  #
  #     a_proc = lambda {|a,b| [a,b] }
  #     a_proc.call(1)   # ArgumentError: wrong number of arguments (given 1, expected 2)
  #
  # See also Proc#lambda?.
  #
  def call: (?) -> untyped

  # <!-- rdoc-file=proc.c -->
  # Invokes the block, setting the block's parameters to the values in *params*
  # using something close to method calling semantics. Returns the value of the
  # last expression evaluated in the block.
  #
  #     a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
  #     a_proc.call(9, 1, 2, 3)    #=> [9, 18, 27]
  #     a_proc[9, 1, 2, 3]         #=> [9, 18, 27]
  #     a_proc.(9, 1, 2, 3)        #=> [9, 18, 27]
  #     a_proc.yield(9, 1, 2, 3)   #=> [9, 18, 27]
  #
  # Note that `prc.()` invokes `prc.call()` with the parameters given.  It's
  # syntactic sugar to hide "call".
  #
  # For procs created using #lambda or `->()` an error is generated if the wrong
  # number of parameters are passed to the proc.  For procs created using Proc.new
  # or Kernel.proc, extra parameters are silently discarded and missing parameters
  # are set to `nil`.
  #
  #     a_proc = proc {|a,b| [a,b] }
  #     a_proc.call(1)   #=> [1, nil]
  #
  #     a_proc = lambda {|a,b| [a,b] }
  #     a_proc.call(1)   # ArgumentError: wrong number of arguments (given 1, expected 2)
  #
  # See also Proc#lambda?.
  #
  alias [] call

  # <!--
  #   rdoc-file=proc.c
  #   - prc.curry         -> a_proc
  #   - prc.curry(arity)  -> a_proc
  # -->
  # Returns a curried proc. If the optional *arity* argument is given, it
  # determines the number of arguments. A curried proc receives some arguments. If
  # a sufficient number of arguments are supplied, it passes the supplied
  # arguments to the original proc and returns the result. Otherwise, returns
  # another curried proc that takes the rest of arguments.
  #
  # The optional *arity* argument should be supplied when currying procs with
  # variable arguments to determine how many arguments are needed before the proc
  # is called.
  #
  #     b = proc {|x, y, z| (x||0) + (y||0) + (z||0) }
  #     p b.curry[1][2][3]           #=> 6
  #     p b.curry[1, 2][3, 4]        #=> 6
  #     p b.curry(5)[1][2][3][4][5]  #=> 6
  #     p b.curry(5)[1, 2][3, 4][5]  #=> 6
  #     p b.curry(1)[1]              #=> 1
  #
  #     b = proc {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
  #     p b.curry[1][2][3]           #=> 6
  #     p b.curry[1, 2][3, 4]        #=> 10
  #     p b.curry(5)[1][2][3][4][5]  #=> 15
  #     p b.curry(5)[1, 2][3, 4][5]  #=> 15
  #     p b.curry(1)[1]              #=> 1
  #
  #     b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) }
  #     p b.curry[1][2][3]           #=> 6
  #     p b.curry[1, 2][3, 4]        #=> wrong number of arguments (given 4, expected 3)
  #     p b.curry(5)                 #=> wrong number of arguments (given 5, expected 3)
  #     p b.curry(1)                 #=> wrong number of arguments (given 1, expected 3)
  #
  #     b = lambda {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
  #     p b.curry[1][2][3]           #=> 6
  #     p b.curry[1, 2][3, 4]        #=> 10
  #     p b.curry(5)[1][2][3][4][5]  #=> 15
  #     p b.curry(5)[1, 2][3, 4][5]  #=> 15
  #     p b.curry(1)                 #=> wrong number of arguments (given 1, expected 3)
  #
  #     b = proc { :foo }
  #     p b.curry[]                  #=> :foo
  #
  def curry: (?int? arity) -> Proc

  # <!--
  #   rdoc-file=proc.c
  #   - prc.hash   ->  integer
  # -->
  # Returns a hash value corresponding to proc body.
  #
  # See also Object#hash.
  #
  def hash: () -> Integer

  # <!--
  #   rdoc-file=proc.c
  #   - prc.lambda? -> true or false
  # -->
  # Returns `true` if a Proc object is lambda. `false` if non-lambda.
  #
  # The lambda-ness affects argument handling and the behavior of `return` and
  # `break`.
  #
  # A Proc object generated by `proc` ignores extra arguments.
  #
  #     proc {|a,b| [a,b] }.call(1,2,3)    #=> [1,2]
  #
  # It provides `nil` for missing arguments.
  #
  #     proc {|a,b| [a,b] }.call(1)        #=> [1,nil]
  #
  # It expands a single array argument.
  #
  #     proc {|a,b| [a,b] }.call([1,2])    #=> [1,2]
  #
  # A Proc object generated by `lambda` doesn't have such tricks.
  #
  #     lambda {|a,b| [a,b] }.call(1,2,3)  #=> ArgumentError
  #     lambda {|a,b| [a,b] }.call(1)      #=> ArgumentError
  #     lambda {|a,b| [a,b] }.call([1,2])  #=> ArgumentError
  #
  # Proc#lambda? is a predicate for the tricks. It returns `true` if no tricks
  # apply.
  #
  #     lambda {}.lambda?            #=> true
  #     proc {}.lambda?              #=> false
  #
  # Proc.new is the same as `proc`.
  #
  #     Proc.new {}.lambda?          #=> false
  #
  # `lambda`, `proc` and Proc.new preserve the tricks of a Proc object given by
  # `&` argument.
  #
  #     lambda(&lambda {}).lambda?   #=> true
  #     proc(&lambda {}).lambda?     #=> true
  #     Proc.new(&lambda {}).lambda? #=> true
  #
  #     lambda(&proc {}).lambda?     #=> false
  #     proc(&proc {}).lambda?       #=> false
  #     Proc.new(&proc {}).lambda?   #=> false
  #
  # A Proc object generated by `&` argument has the tricks
  #
  #     def n(&b) b.lambda? end
  #     n {}                         #=> false
  #
  # The `&` argument preserves the tricks if a Proc object is given by `&`
  # argument.
  #
  #     n(&lambda {})                #=> true
  #     n(&proc {})                  #=> false
  #     n(&Proc.new {})              #=> false
  #
  # A Proc object converted from a method has no tricks.
  #
  #     def m() end
  #     method(:m).to_proc.lambda?   #=> true
  #
  #     n(&method(:m))               #=> true
  #     n(&method(:m).to_proc)       #=> true
  #
  # `define_method` is treated the same as method definition. The defined method
  # has no tricks.
  #
  #     class C
  #       define_method(:d) {}
  #     end
  #     C.new.d(1,2)       #=> ArgumentError
  #     C.new.method(:d).to_proc.lambda?   #=> true
  #
  # `define_method` always defines a method without the tricks, even if a
  # non-lambda Proc object is given. This is the only exception for which the
  # tricks are not preserved.
  #
  #     class C
  #       define_method(:e, &proc {})
  #     end
  #     C.new.e(1,2)       #=> ArgumentError
  #     C.new.method(:e).to_proc.lambda?   #=> true
  #
  # This exception ensures that methods never have tricks and makes it easy to
  # have wrappers to define methods that behave as usual.
  #
  #     class C
  #       def self.def2(name, &body)
  #         define_method(name, &body)
  #       end
  #
  #       def2(:f) {}
  #     end
  #     C.new.f(1,2)       #=> ArgumentError
  #
  # The wrapper *def2* defines a method which has no tricks.
  #
  def lambda?: () -> bool

  # <!--
  #   rdoc-file=proc.c
  #   - prc.parameters(lambda: nil)  -> array
  # -->
  # Returns the parameter information of this proc.  If the lambda keyword is
  # provided and not nil, treats the proc as a lambda if true and as a non-lambda
  # if false.
  #
  #     prc = proc{|x, y=42, *other|}
  #     prc.parameters  #=> [[:opt, :x], [:opt, :y], [:rest, :other]]
  #     prc = lambda{|x, y=42, *other|}
  #     prc.parameters  #=> [[:req, :x], [:opt, :y], [:rest, :other]]
  #     prc = proc{|x, y=42, *other|}
  #     prc.parameters(lambda: true)  #=> [[:req, :x], [:opt, :y], [:rest, :other]]
  #     prc = lambda{|x, y=42, *other|}
  #     prc.parameters(lambda: false) #=> [[:opt, :x], [:opt, :y], [:rest, :other]]
  #
  def parameters: (?lambda: boolish) -> Method::param_types

  # <!--
  #   rdoc-file=proc.c
  #   - prc.source_location  -> [String, Integer]
  # -->
  # Returns the Ruby source filename and line number containing this proc or `nil`
  # if this proc was not defined in Ruby (i.e. native).
  #
  def source_location: () -> [String, Integer]?

  # <!--
  #   rdoc-file=proc.c
  #   - prc.to_proc -> proc
  # -->
  # Part of the protocol for converting objects to Proc objects. Instances of
  # class Proc simply return themselves.
  #
  def to_proc: () -> self

  # <!--
  #   rdoc-file=proc.c
  #   - prc.to_s   -> string
  # -->
  # Returns the unique identifier for this proc, along with an indication of where
  # the proc was defined.
  #
  def to_s: () -> String

  # <!-- rdoc-file=proc.c -->
  # Returns the unique identifier for this proc, along with an indication of where
  # the proc was defined.
  #
  alias inspect to_s
end
