# A `Proc` represents a function pointer with an optional context (the closure data). # It is typically created with a proc literal: # # ``` # # A proc without arguments # -> { 1 } # Proc(Int32) # # # A proc with one argument # ->(x : Int32) { x.to_s } # Proc(Int32, String) # # # A proc with two arguments: # ->(x : Int32, y : Int32) { x + y } # Proc(Int32, Int32, Int32) # ``` # # See [`Proc` literals](https://crystal-lang.org/reference/syntax_and_semantics/literals/proc.html) in the language reference. # # The types of the arguments (`T`) are mandatory, except when directly # sending a proc literal to a lib fun in C bindings. # # The return type (`R`) is inferred from the proc's body. # # A special new method is provided too: # # ``` # Proc(Int32, String).new { |x| x.to_s } # Proc(Int32, String) # ``` # # This form allows you to specify the return type and to check it # against the proc's body. # # Another way to create a `Proc` is by capturing a block: # # ``` # def capture(&block : Int32 -> Int32) # # block argument is used, so block is turned into a Proc # block # end # # proc = capture { |x| x + 1 } # Proc(Int32, Int32) # proc.call(1) # => 2 # ``` # # When capturing blocks, the type of the arguments and return type # must be specified in the capturing method block signature. # # ### Passing a Proc to a C function # # Passing a `Proc` to a C function, for example as a callback, is possible # as long as the `Proc` isn't a closure. If it is, either a compile-time or # runtime error will happen depending on whether the compiler can check this. # The reason is that a `Proc` is internally represented as two void pointers, # one having the function pointer and another the closure data. If just # the function pointer is passed, the closure data will be missing at invocation time. # # Most of the time a C function that allows setting a callback also provide # an argument for custom data. This custom data is then sent as an argument # to the callback. For example, suppose a C function that invokes a callback # at every tick, passing that tick: # # ``` # lib LibTicker # fun on_tick(callback : (Int32, Void* ->), data : Void*) # end # ``` # # To properly define a wrapper for this function we must send the `Proc` as the # callback data, and then convert that callback data to the `Proc` and finally invoke it. # # ``` # module Ticker # # The callback for the user doesn't have a Void* # @@box = Pointer(Void).null # # def self.on_tick(&callback : Int32 ->) # # Since Proc is a {Void*, Void*}, we can't turn that into a Void*, so we # # "box" it: we allocate memory and store the Proc there # boxed_data = Box.box(callback) # # # We must save this in Crystal-land so the GC doesn't collect it (*) # @@box = boxed_data # # # We pass a callback that doesn't form a closure, and pass the boxed_data as # # the callback data # LibTicker.on_tick(->(tick, data) { # # Now we turn data back into the Proc, using Box.unbox # data_as_callback = Box(typeof(callback)).unbox(data) # # And finally invoke the user's callback # data_as_callback.call(tick) # }, boxed_data) # end # end # # Ticker.on_tick do |tick| # puts tick # end # ``` # # Note that we save the box in `@@box`. The reason is that if we don't do it, # and our code doesn't reference it anymore, the GC will collect it. # The C library will of course store the callback, but Crystal's GC has # no way of knowing that. struct Proc {% if flag?(:interpreted) %} @[Primitive(:interpreter_proc_new)] {% end %} def self.new(pointer : Void*, closure_data : Void*) : self func = {pointer, closure_data} ptr = pointerof(func).as(self*) ptr.value end # Creates a `Proc` by capturing the given *block*. # # The block argument types are inferred from the `Proc`'s type arguments. The # return type of the block must match the return type specified in the `Proc` # type. # # ``` # gt = Proc(Int32, Int32, Bool).new do |x, y| # x > y # end # gt.call(3, 1) # => true # gt.call(1, 2) # => false # ``` def self.new(&block : self) block end # Returns a new `Proc` that has its first arguments fixed to # the values given by *args*. # # See [Wikipedia, Partial application](https://en.wikipedia.org/wiki/Partial_application) # # ``` # add = ->(x : Int32, y : Int32) { x + y } # add.call(1, 2) # => 3 # # add_one = add.partial(1) # add_one.call(2) # => 3 # add_one.call(10) # => 11 # # add_one_and_two = add_one.partial(2) # add_one_and_two.call # => 3 # ``` def partial(*args : *U) forall U {% begin %} {% remaining = (T.size - U.size) %} ->( {% for i in 0...remaining %} arg{{i}} : {{T[i + U.size]}}, {% end %} ) { call( *args, {% for i in 0...remaining %} arg{{i}}, {% end %} ) } {% end %} end # Returns the number of arguments of this `Proc`. # # ``` # add = ->(x : Int32, y : Int32) { x + y } # add.arity # => 2 # # neg = ->(x : Int32) { -x } # neg.arity # => 1 # ``` def arity {{T.size}} end def pointer : Void* internal_representation[0] end def closure_data : Void* internal_representation[1] end def closure? : Bool !closure_data.null? end private def internal_representation func = self ptr = pointerof(func).as({Void*, Void*}*) ptr.value end def ==(other : self) pointer == other.pointer && closure_data == other.closure_data end def ===(other : self) self == other end def ===(other) call(other) end # See `Object#hash(hasher)` def hash(hasher) internal_representation.hash(hasher) end def clone self end def to_s(io : IO) : Nil io << "#<" io << {{@type.name.stringify}} io << ":0x" pointer.address.to_s(io, 16) if closure? io << ":closure" end io << '>' nil end # Invokes this `Proc` and returns the result. # # ``` # add = ->(x : Int32, y : Int32) { x + y } # add[1, 2] # => 3 # ``` # # Same as `#call`. def [](*args : *T) : R call(*args) end end