alias MyTuple = NamedTuple(
foo: String,
)
def set(**values : MyTuple)
pp! values
end
set(foo: "bar")
Error in line 9: no overload matches 'set', foo: String
Overloads are:
- set(**values : MyTuple)
alias MyTuple = Tuple(String)
def foo(*values : MyTuple)
pp! values
end
foo("foo")
Error in line 7: no overload matches 'foo' with type String
Overloads are:
- foo(*values : MyTuple)
It would allow to avoid big and ugly macros involvement.
My usecase is ORM Query builder with #where(id: 42, name: "John") like methods, which are wanted to be defined as following:
class User
include Model
# Generated in macro for each model
alias Types = NamedTuple(id: Int32, name: String)
end
class Query(Model)
def where(**values : Model::Types)
end
end
Use **MyTuple in the restriction
This is explained a bit in https://crystal-lang.org/docs/syntax_and_semantics/type_restrictions.html although a section is missing for double splats.
Basically, when you have:
def foo(**args : SomeType)
end
it means all types in args must be SomeType. For example:
def foo(**args : Int32)
end
foo x: 1, y: 2 # OK, all types are Int32
foo x: 1, y: "oops" # Error, one of the types is not Int32
If you want to restrict the whole named arguments value to some type, use ** in front of the type restriction:
def foo(**args : **{x: Int32, y: String})
end
foo x: 1, y: "foo" # OK
foo x: 1, y: 1 # Error, y must be an Int32
Of course in the last snippet you can use an alias:
alias MyType = {x: Int32, y: String}
def foo(**args : **MyType)
end
foo x: 1, y: "foo" # OK
Please close this issue if the above fixes your problem.
By the way, I think the above won't be helpful to you because you can't omit elements from the named tuple. But I wouldn't say this is a bug. You might need to code your code in a different way.
I'll close this because the original use case is solved.
@asterite is it possible to acquire values in compile time in def where(**values)?
Update: I mean, it's a NamedTuple and has to be known in the compile time:
def where(**values)
pp! values.class
end
# => NamedTuple(foo: String)
def where(**values : **T) forall T
# Now you can use T, and inspect it at compile time
{% puts T.keys %}
end
Most helpful comment
tl;dr
Use
**MyTuplein the restrictionExplanation
This is explained a bit in https://crystal-lang.org/docs/syntax_and_semantics/type_restrictions.html although a section is missing for double splats.
Basically, when you have:
it means all types in
argsmust beSomeType. For example:If you want to restrict the whole named arguments value to some type, use
**in front of the type restriction:Of course in the last snippet you can use an alias:
Please close this issue if the above fixes your problem.