Search code examples
crystal-lang

Is it possible to pass a Proc into a function?


I'm trying to implement Ruby's function composition operator << into Crystal's proc. In Ruby it seems pretty straight forward.

  def << block
    proc { |*args| self.call( block.to_proc.call(*args) ) }
  end
end

I've tried to do something similar.

struct Proc
  def <<(&block)
    Proc.new { |*args, blk| call(block.call(*args, blk)) }
  end
end

I've tried testing it with a simple adder and suber function

def add(x : Int32)
  x + 1
end

def sub(x : Int32)
  x - 1
end

But, I'm getting this error. Error: wrong number of arguments for 'Proc(Int32, Int32)#<<' (given 1, expected 0)

I've also tried changing the << to take in a proc but that also results in expected block type to be a function type, not Proc(*T, R)

I'm kind of new to the language so I'm not too sure what knowledge I'm missing to understand why this is not working.


Solution

  • You get this error because Proc is underspecified. The Proc type is a generic type and needs to be instantiated with specific generic arguments that describe the types of its parameters and the return type.

    You can see the same behaviour with a minimal example:

    Proc.new { 1 } # Error: expected block type to be a function type, not Proc(*T, R)
    

    Granted, the error message is not very telling.


    A working example for what you try to achieve could look like this:

    struct Proc
      def <<(block : Proc(*U, V)) forall U, V
        Proc(*T, V).new { |arg| call(block.call(arg)) }
      end
    end
    
    def add(x : Int32)
      x + 1
    end
     
    def sub(x : Int32)
      x - 1
    end
    
    x = ->add(Int32) << ->sub(Int32)
    p! x.call(10)