Crystal: Macro could not use default values in named arguments after an splat

Created on 16 Jun 2016  路  7Comments  路  Source: crystal-lang/crystal

I want to know if i'm using correctly the splats with named arguments for macros.

require "http/server"

abstract class Filter
  def call(context : HTTP::Context)
  end
end

macro get(*routes, before, after = ([] of Filter | Filter | Nil), &block : -> T)
  pp "test"
end

get "/v2/security/users/", param_test : String do |ctx|
  pp "hello"
end

# get "/v2/security/users/", param_test : String, before: nil, after: nil do |ctx|
#   pp "hello"
# end

I'm working on an experimental web framework. The code in the comments works ok but the uncommented one throws this error:

declaring the type of a local variable is not yet supported
_get "/v2/security/users/", param_test : String do |ctx|_

question compiler

All 7 comments

It's definitely inconsistent, however I'm afraid my argument will go into the opposite direction than what you would like to see.

I would say both cases should error, given that declaring the type of a local variable is not yet valid, so shouldn't doing it be syntactically anywhere. Neither variant should produce a valid AST node for param_test : String at any point, both should give a syntax error.

@jhass, it's nice to see that point of view, this way i don't lose my time with the new web framework.
I'll love to have this kind of syntax for autoloading entities from databases based on the TypeDeclaration and adding the name of the variable to the http context so it could be used in the block.

require "http/server"

class User
   property id
   def initialize(param : RouteParam)
     @id = param
     pp "load param with the id: #{param}"
   end
end

abstract class Filter
  def call(context : HTTP::Context)
  end
end

macro get(*routes, before, after = ([] of Filter | Filter | Nil), &block : -> T)
  #TODO: Autoloading of route and params
end

get "/v2/security/user/", user : User, "/edit" do |ctx|
  pp ctx.user.id
end

Thanks for the feedback.

Take note that it's just my opinion, it's not decided yet. Also take note that both should work when declaring the type of locals will actually be possible (do we have an issue to track this we can link back to here?).

To offer an alternative, parsing named tuple literals like JSON.mapping etc. do should be even easier and well supported.

Not a bug:

macro get(*routes, before, after = ([] of Filter | Filter | Nil), &block : -> T)
  pp "test"
end

get "/v2/security/users/", param_test : String do |ctx|
  pp "hello"
end

The get macro expects a required before argument that you are not passing. So, get doesn't match that macro and is tried to be executed as a method call. For method calls we first type its arguments, and then you get an error about param_test.

This works:

macro get(*routes, before = nil, after = ([] of Filter | Filter | Nil), &block : -> T)
  pp "test"
end

get "/v2/security/users/", param_test : String do |ctx|
  pp "hello"
end

Side note: not sure what you mean with [] of Filter | Filter | Nil, that's basically [] of Filter | Nil.

@asterite, thanks for the clarification, confirmed as not a bug. I use [] of Filter | Filter | Nil because i want be able to do this:

get "/v2/security/users/", param_test : String, before: AuthenticationFilter, after: [FormatFilter, PoolDetachFilter] do |ctx|
  pp "hello"
end
  • Sometimes you'll only have 1 filter in the before or after chain. Is there any other way to do that?

But [] of Filter | Filter | Nil is parsed as [] of (Filter | Filter | Nil). Plus, it doesn't make much sense as a default value (it makes sense as a type restriction).

My bad, i'm still learning developing with macros and type restrictions. I'll move the type restriction to the routes loader generated functions. Thanks for all your help.
Everyday i'm loving more to work with crystal and experimenting all the new features.

Was this page helpful?
0 / 5 - 0 ratings