Search code examples
crystal-langsplat

Is there a way to use non Tuple objects with splat operator in Crystal?


Is there some function or syntax construction to make next examples work?

Invoke Hash#values_at function with an Array argument:

h = {"a" => 1, "b" => 2, "c" => 3}
ary = ["a", "b"]
h.values_at(*ary) # Error: argument to splat must be a tuple, not Array(String)

Pass Hash to initialize class or struct:

struct Point                                                                                                 
    def initialize(@x : Int32, @y : Int32)                                                                     
    end                                                                                                        
end
h = {"x" => 1, "y" => 2}
Point.new(**h) # Error: argument to double splat must be a named tuple, not Hash(String, Int32)

Solution

  • The first example might be impossible, depending on the circumstances. However if the length of elements is fixed, you can do:

    h = {"a" => 1, "b" => 2, "c" => 3}
    ary = ["a", "b"]
    p h.values_at(*{String, String}.from(ary))
    

    https://carc.in/#/r/3oot See Tuple.from

    NamedTuple supports the same approach:

    struct Point                                                                                                 
        def initialize(@x : Int32, @y : Int32)                                                                     
        end                                                                                                        
    end
    h = {"x" => 1, "y" => 2}
    
    p Point.new(**{x: Int32, y: Int32}.from(h))
    

    https://carc.in/#/r/3oov See NamedTuple.from

    Both of these are just some sugar around ensuring types and decomposing the structure manually at runtime and mainly useful when your data comes from an external source, such as being parsed from JSON.

    Of course it's preferred to create and use Tuple over Array and NamedTuple over Hash in the first place where possible.