Crystal能否将Proc传入函数?如何实现Proc的<<函数组合运算符
Crystal实现Proc组合运算符
<<的解决方案 错误原因分析
两个报错均源于Crystal和Ruby的核心差异:Crystal是静态类型语言,Proc带有明确的签名约束,且参数传递规则和Ruby不同:
- 第一个报错
Error: wrong number of arguments for 'Proc(Int32, Int32)#<<' (given 1, expected 0):你定义<<方法时用了&block参数,该语法仅用于捕获调用时的块字面量,不支持接收已定义的Proc实例作为位置参数。 - 第二个报错
expected block type to be a function type, not Proc(*T, R):修改为接收Proc参数时未声明泛型约束,Crystal类型系统无法推导传入Proc和当前Proc的签名匹配关系,导致类型校验失败。
正确实现代码
扩展Proc结构体时显式声明泛型约束,适配不同签名的Proc组合需求:
struct Proc(*T, R) # 组合规则:self << g 等价于 输入参数先传入g执行,返回值再传入self执行 def <<(g : Proc(*T, U)) forall U Proc(*T, R).new do |*args, blk| call(g.call(*args, blk)) end end end
测试用例
Crystal不会自动将顶层方法转为Proc,需要显式捕获方法为Proc实例后再组合:
# 定义基础函数 def add(x : Int32) x + 1 end def sub(x : Int32) x - 1 end # 显式捕获方法为Proc实例 add_proc = ->add(Int32) sub_proc = ->sub(Int32) # 组合函数:先执行sub,结果传入add执行 combined = add_proc << sub_proc puts combined.call(5) # 输出结果为5,等价于 add(sub(5)) = (5-1)+1 =5
可选优化:支持块字面量传入
如果需要兼容直接传入块的写法,可以新增方法重载:
struct Proc(*T, R) # 支持传入块字面量的重载 def <<(&g : *T -> U) forall U self << Proc.new(&g) end def <<(g : Proc(*T, U)) forall U Proc(*T, R).new do |*args, blk| call(g.call(*args, blk)) end end end
重载后两种写法都可以正常使用:
# 写法1:传入Proc实例 combined = add_proc << sub_proc # 写法2:直接传入块字面量 combined = add_proc << |x| x - 1
额外说明
由于Crystal是静态类型,组合的两个Proc必须满足类型匹配规则:传入<<的Proc的返回值类型,必须和当前Proc的入参类型一致,否则编译阶段就会抛出类型不匹配错误,这是和Ruby动态实现的核心差异。
内容的提问来源于stack exchange,提问作者sneakyfishies
相关产品推荐
相关产品推荐

