The Crystal Programming Language Forum

Obj.as(obj.class) ^ Error: expecting token 'CONST', not 'block'

Hi! I have a problem with the type handling… :frowning:

@blocks : Array(T) # Array(Gusano::Block(Int32, Nil) | Gusano::Block(String, Nil))

def start(params)
  block = @blocks.find { |el| el.name == @actual_block }

  if block.nil?
    # TODO
  else
    block.as(block.class).run params # <- Error
  end
end

I need to cast block to its real type. @blocks is an array and the objects inside are instances of a generic class so I get this error:

Error: no overload matches 'Proc(Tuple(String), Nil)#call' with type Tuple(Int32)

Overloads are:
 - Proc(T, R)#call(*args : *T)

Could anybody help me? Thank you! :confused:

The code is a bit too much of fragment to really tell what’s going on and what you really want.

The error is a conceptual one. block.class is a runtime value, however .as operates (at least partly) in compile time. It needs to generate the right native code for the call with the right signature after all.

I’m sure this code produces a syntax error. block.as(block.class) is invalid. The argument to as must be a constant.
There should be no way this could possible parse. Just parsing the snipped you provided without any context shows that syntax error.

Hi! Thank you for your help… I have uploaded the simplified version of the code to the official online compiler:

https://play.crystal-lang.org/#/r/9143

class Block(*T, U)
  @exec_fn : Proc(T, Void)
  getter name
 
  def initialize(@name : Symbol, &fn : *T -> U)
    @exec_fn = ->(params : T) { fn.call *params }
  end
 
  def run(*params)
    @exec_fn.call params
  end
end
 
class Engine(T)
  @actual_block : Symbol
  @blocks : Array(T)
 
  def initialize(@blocks : Array(T), @actual_block : Symbol); end
 
  def start(params)
    block = @blocks.find { |el| el.name == @actual_block }
 
    if !block.nil?
      block.run params
    end
  end
end
 
block1 = Block(Int32, String).new :init { |a| a.to_s }
block2 = Block(String, Int32).new :end { |str| str.to_i }
 
engine = Engine.new(
  [
    block1,
    block2,
  ], 
  :init
)
 
engine.start 123

Output:

Showing last frame. Use --error-trace for full trace.

error in line 10
Error: no overload matches 'Proc(Tuple(String), Int32)#call' with type Tuple(Int32)

Overloads are:
 - Proc(T, R)#call(*args : *T)

The @blocks array is: Array(Gusano::Block(Int32, Nil) | Gusano::Block(String, Nil)). So, the block type on the #start function is Gusano::Block(Int32, Nil) | Gusano::Block(String, Nil).

I think that might be the problem.

There’s no way to it that way.

You sure can get interesting error messages after playing for a while

Error: no overload matches 'Block(Tuple(Int32))#run' with type Tuple(Int32)

Overloads are:
 - Block(T)#run(params : T)

https://play.crystal-lang.org/#/r/914w

1 Like

:frowning:

I find that error a bit confusing. I have seen the modifications you made and it.

And after playing for a while:

block.as(Block(typeof(params))).run params 
# Error: can't cast (Block(Int32) | Block(String)) to Block(Tuple(Int32))

I think I’ll pause this. I’ve been at this for several hours and I still don’t fully understand the type system :( Thank you!

What I mean is, the way you are coding it will never work, but the program you want to build is definitely possible.

Could you explain what are you trying to build?

1 Like

My idea is to have blocks that execute a function (proc) and that can be connected to each other…

Block A, B

A(Input).run
B(A.output).run

I think it could be useful for visual programming, some rpc tool or even to create a Workflow Automation tool.

This might be possible in the future if there’s a way to do if x.is_a?(Proc(Int32, _)), but right now you can’t use underscore in is_a?. Then you could do something like:

  def start(params : *T) forall T
    block = @blocks.find { |el| el.name == @actual_block }
 
    if block.is_a?(Proc(*T, _))
      block.run params
    end
  end

I made underscore kind of work during the weekend by hacking something, but then something else breaks and then I ran out of time. I doubt this will be fixed before 1.0.

What you want to do is definitely possible if you model it in a different way. But exactly like you are doing it here will never work due to language limitations.

1 Like