Proc(*T, R) - Crystal 1.21.0

# struct Proc(*T, R)

- Proc(*T, R)
- Value
- Object

##
Overview

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 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.

##
Defined in:

primitives.cr
proc.cr

##
Constructors

- .new(pointer : Pointer(Void), closure_data : Pointer(Void)) : self
- .new(&block : self)

  Creates a `Proc` by capturing the given block.

##
Instance Method Summary

- #==(other : self)
- #===(other : self)
- #===(other)

  Case equality.

- #[](*args : *T) : R

  Invokes this `Proc` and returns the result.

- #arity

  Returns the number of arguments of this `Proc`.

- #call(*args : *T) : R

  Invokes this `Proc` and returns the result.

- #clone
- #closure? : Bool
- #closure_data : Pointer(Void)
- #hash(hasher)

  See `Object#hash(hasher)`

- #partial(*args : *U) forall U

  Returns a new `Proc` that has its first arguments fixed to the values given by args.

- #pointer : Pointer(Void)
- #to_s(io : IO) : Nil

  Prints a nicely readable and concise string representation of this object, typically intended for users, to io.

### Instance methods inherited from struct `Value`

==(other : Log::Metadata::Value)
==(other : JSON::Any)
==(other : YAML::Any)
==(other) ==, dup dup

### Instance methods inherited from class `Object`

! : Bool !, !=(other) !=, !~(other) !~, ==(other) ==, ===(other : JSON::Any)
===(other : YAML::Any)
===(other) ===, =~(other) =~, as(type : Class) as, as?(type : Class) as?, class class, dup dup, hash(hasher)
hash hash, in?(collection : Object) : Bool
in?(*values : Object) : Bool in?, inspect(io : IO) : Nil
inspect : String inspect, is_a?(type : Class) : Bool is_a?, itself itself, nil? : Bool nil?, not_nil!(message)
not_nil! not_nil!, pretty_inspect(width = 79, newline = "\n", indent = 0) : String pretty_inspect, pretty_print(pp : PrettyPrint) : Nil pretty_print, responds_to?(name : Symbol) : Bool responds_to?, tap(&) tap, to_json(io : IO) : Nil
to_json : String to_json, to_pretty_json(indent : String = " ") : String
to_pretty_json(io : IO, indent : String = " ") : Nil to_pretty_json, to_s(io : IO) : Nil
to_s : String to_s, to_yaml(io : IO) : Nil
to_yaml : String to_yaml, try(&) try, unsafe_as(type : T.class) forall T unsafe_as

### Class methods inherited from class `Object`

from_json(string_or_io : String | IO, root : String)
from_json(string_or_io : String | IO) from_json, from_yaml(string_or_io : String | IO) from_yaml

### Macros inherited from class `Object`

class_getter(*names, &block) class_getter, class_getter!(*names) class_getter!, class_getter?(*names, &block) class_getter?, class_property(*names, &block) class_property, class_property!(*names) class_property!, class_property?(*names, &block) class_property?, class_setter(*names) class_setter, def_clone def_clone, def_equals(*fields) def_equals, def_equals_and_hash(*fields) def_equals_and_hash, def_hash(*fields) def_hash, delegate(*methods, to object) delegate, forward_missing_to(delegate) forward_missing_to, getter(*names, &block) getter, getter!(*names) getter!, getter?(*names, &block) getter?, property(*names, &block) property, property!(*names) property!, property?(*names, &block) property?, setter(*names) setter

##
Constructor Detail

def self.new(pointer : Pointer(Void), closure_data : Pointer(Void)) : self #
def self.new(&block : self) #

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

##
Instance Method Detail

def ==(other : self) #
def ===(other : self) #
def ===(other) #
Description copied from class Object

Case equality.

The `#===` method is used in a `case ... when ... end` expression.

For example, this code:

    case value
    when x
      # something when x
    when y
      # something when y
    end

Is equivalent to this code:

    if x === value
      # something when x
    elsif y === value
      # something when y
    end

Object simply implements `#===` by invoking `#==`, but subclasses (notably `Regex`) can override it to provide meaningful case-equality semantics.

def [](*args : *T) : R #

Invokes this `Proc` and returns the result.

    add = ->(x : Int32, y : Int32) { x + y }
    add[1, 2] # => 3

Same as `#call`.

def arity #

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 call(*args : *T) : R #

Invokes this `Proc` and returns the result.

    add = ->(x : Int32, y : Int32) { x + y }
    add.call(1, 2) # => 3

def clone #
def closure? : Bool #
def closure_data : Pointer(Void) #
def hash(hasher) #

See `Object#hash(hasher)`

def partial(*args : *U) forall U #

Returns a new `Proc` that has its first arguments fixed to the values given by args.

See Wikipedia, 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 pointer : Pointer(Void) #
def to_s(io : IO) : Nil #
Description copied from class Object

Prints a nicely readable and concise string representation of this object, typically intended for users, to io.

This method is called when an object is interpolated in a string literal:

    "foo #{bar} baz" # calls bar.to_io with the builder for this string

`IO#<<` calls this method to append an object to itself:

    io << bar # calls bar.to_s(io)

Thus implementations must not interpolate `self` in a string literal or call `io << self` which both would lead to an endless loop.

Also see `#inspect(IO)`.