如何强制返回值与Crystal中的参数值相同类型?

时间:2019-05-20 12:41:11

标签: crystal-lang

如果我有一个接受多种类型参数的函数,如何强制返回必须与输入值匹配?

尤其是当我希望一种方法可以处理任何父类型的子代时,就会出现这种情况。为了演示,请考虑“条状”的东西:

abstract struct Barlike
  property bar: Int32
  def initialize(@bar); end
  end
  abstract def make_clang_sound
  abstract def serve_drinks
end

现在任何结构都可以实现这两种方法,并存储该值

struct Bar1 < Barlike
  def make_clang_sound
    puts "bing bang bong"
  end

  def serve_drinks
    puts "your drink sir"
  end
end

struct Bar2 < Barlike
  def make_clang_sound
    puts "kling klang"
  end

  def serve_drinks
    puts "here are your drinks"
  end
end

现在,如果我有一种方法想要使用该条并返回具有更新值的新方法(毕竟是结构),那么该怎么办?

def foo(arg : Barlike)
  new_bar = arg.bar + 2
  arg.class.new(new_bar)
end

如果传入Bar1,则返回Bar1;如果传入Bar2,则返回def foo(arg : Barlike) "howdy" end ,但不能保证:

foo

我也将我的foo放入一个抽象结构中,因此我需要保证Barlike的实现者返回给定的相同类型的def foo(arg : Barlike) : arg.class end

我尝试过

def foo(arg : Barlike) : typeof(arg)
end

但这是一个编译时错误(不能在那里使用arg)

我也尝试过

fields <- gBuffer(fields, byid=TRUE, width=0) # Expands the given geometry to include 
the area within the specified width 

zone <- fields[fields$Type == "Zone", ]
plot <- fields[fields$Type == "Plot", ]

d <- erase(zone, plot)
spplot(d, "Rx")

r <- bind(plot, d)

rd <- aggregate(r, "Rx")

spplot(rd, "Rx")

通过了,但是这里的typeof只是Barlike,而我真的需要它仅是传入的东西,只有Bar1或Bar2,等等。

宏可以帮忙吗?

2 个答案:

答案 0 :(得分:2)

用于此目的的工具为free variables。这实际上是泛型的一种方法。

# This method returns the same type as its argument
def foo(arg : T) : T forall T
  arg
end

这已经可以解决您问题的主要部分。

但是,当前无法对自由变量应用类型限制,例如将T限制为Barlike

但是有一些解决方法:

  1. 使用宏来验证参数类型:
def foo(arg : T) : T forall T
  {% raise "arg must implement Barlike" unless T < Barlike %}
  arg
end
  1. 委托具有类型限制的另一种方法
def foo(arg : T) : T forall T
  foo_impl(arg)
end

private def foo_impl(arg : Barlike)
  arg
end

两种解决方法都会影响该方法的实现。无法为abstract def指定这种类型限制。如果将foo_impl抽象化并要求继承类来实现,而不是foo,则数字2可能是可行的。 但是,也可以只使用带有自由变量的初始示例,而没有Barlike限制。实际上,您可能不会获得太多收益。

答案 1 :(得分:1)

这是可行的方法:

{% for sub in Barlike.subclasses %}
  struct {{sub}}
    def foo() : {{sub}}
      {{sub}}.new(@bar+1)
    end
  end
{% end %}

full example

但是感觉像是在试图解决错误的问题。它使用#subclasses宏为所有子结构生成一个foo。

您也可以在抽象类example中将它们声明为self方法。