From 712667d109fa2b6d9bfbf5f9b56dfe0e9fc0502d Mon Sep 17 00:00:00 2001 From: SamW Date: Sat, 20 Jul 2024 11:20:29 -0700 Subject: [PATCH] Update Proc --- core/proc.rbs | 207 ++++++++++++++++++++++++++++++++++----- test/stdlib/Proc_test.rb | 170 +++++++++++++++++++++++--------- 2 files changed, 305 insertions(+), 72 deletions(-) diff --git a/core/proc.rbs b/core/proc.rbs index 22f0f87fa..d09ec7feb 100644 --- a/core/proc.rbs +++ b/core/proc.rbs @@ -288,8 +288,184 @@ # # Numbered parameters were introduced in Ruby 2.7. # -class Proc < Object - def clone: () -> self +class Proc + interface _Callable + def call: (*untyped, **untyped) ?{ (*untyped, **untyped) -> untyped } -> untyped + end + + # + # 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, **untyped) -> untyped } -> instance + + def clone: () -> instance + def dup: () -> instance + + # + # 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 + + # + # 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 + + # + # 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 + + # + # 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 + + # + # 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 + + # + # 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? == # # Invokes the block, setting the block's parameters to the values in *params* @@ -408,7 +584,7 @@ class Proc < Object # # See also Proc#lambda?. # - def []: (*untyped arg0) -> untyped + alias [] call # - # 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 initialize: () { (?) -> untyped } -> void - #