-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Splat tuple inside tuple literal #3718
Comments
t = {1, "bla"}
t2 = {"a", *t}
p t2 Yes, I think I also wanted to do that at least one time. I'll mark this as an enhancement (I'll change the title a bit) |
Workaround: t = {1, "bla"}
t2 = Tuple.new("a", *t)
p t2 Which makes me believe that the compiler could simply make |
This was raised on Gitter again, so I'll summarize what I believe each of the literals would look like if splat expansions are allowed inside them: t = {1, 'a'}
t2 = {true, *t} # => {true, 1, 'a'}
typeof(t2) # => Tuple(Bool, Int32, Char)
def f(x : {Bool, *{Int32, Char}}); end # should be okay
def f(x : Tuple(Bool, *{Int32, Char})); end # okay
[exp1, *exp2, *exp3, exp4]
# the above is equivalent to:
ary = ::Array(typeof(exp1, exp2.first, exp3.first, exp4)).new
ary << exp1
ary.concat(exp2) # faster than `each` loop if `exp2` is also an `Array`
ary.concat(exp3)
ary << exp4
ary # `#concat` and `#<<` both return `self` so maybe this can be dropped
T{exp1, *exp2, *exp3, exp4}
# the above is equivalent to:
ary = T(typeof(exp1, exp2.first, exp3.first, exp4)).new
ary << exp1
exp2.each { |elem| ary << elem }
exp3.each { |elem| ary << elem }
ary << exp4
ary Array-like literal. Likewise, we can do the same to key-value literals if we support t = {y: 1, z: 'a'}
t2 = {x: true, **t} # => {x: true, y: 1, z: 'a'}
typeof(t2) # => NamedTuple(x: Bool, y: Int32, z: Char)
t3 = {**t, x: true} # => {y: 1, z: 'a', x: true}
typeof(t3) # => NamedTuple(y: Int32, z: Char, x: Bool)
{**t, y: ""} # => {y: "", z: 'a'}
{y: "", **t} # => {y: 1, z: 'a'}
{key1 => exp1, **exp2, **exp3, key4 => exp4}
# the above is equivalent to:
hsh = ::Hash(
typeof(key1, exp2.keys.first, exp3.keys.first, key4),
typeof(exp1, exp2.values.first, exp3.values.first, exp4),
).new
hsh[key1] = exp1
exp2.each { |k, v| hsh[k] = v } # `Hash#merge!` provides no optimizations over this
exp3.each { |k, v| hsh[k] = v }
hsh[key4] = exp4
hsh
If it were an {1 => 'a', **[[2, 'b'], [3, 'c']]} # TypeError (no implicit conversion of Array into Hash)
class Array
def to_hash; to_h; end
end
{1 => 'a', **[[2, 'b'], [3, 'c']]} # => {1=>"a", 2=>"b", 3=>"c"}
[1, *"abc"] # => [1, "abc"]
class String
def to_a; chars; end
end
[1, *"abc"] # => [1, "a", "b", "c"] |
Splatting tuples would allow to replace the macros in I'm really not sure about |
I think we can start with Tuple and Array-like types. When I thought about this I forgot we need We can probably introduce a convention to mean "this can be array-splatted" and "this can be hash-splatted". Then array-splatted types need class Indexable
def to_array_splat
self
end
end
class Hash
def to_hash_splat
self
end
end
class NamedTuple
def to_hash_splat
self
end
def first
# something that returns a tuple with two elements
end
end That way splatting Array into Hash will not compile. And trying to unsplat or splat a String also won't work. |
What's the rationale for EDIT: Yes 1-to-n multiple assignment will need Even def __crystal_first(x)
x.each { |elem| return elem }
raise ""
end
[*exp] # calls ::Array(typeof(::__crystal_first(exp.to_array_splat))).new This way we don't have to rely on |
@HertzDevil sounds good. Yes, I was thinking size is only needed when unpacking |
|
|
Hm, why not |
But now I'm worrying about whether existing methods that rely on EDIT: {1, 'a'}.reject(Int32) # Error: no overload matches 'Array(NoReturn)#<<' with type Char
{ {1, 'a'}, {"", true} }.to_h # Error: no overload matches 'Hash(Int32, Char)#[]=' with types (Int32 | String), Char
[{1, 'a'}].transpose # Error: expected block to return T, not (Char | Int32)
struct NamedTuple
def map
array = Array(typeof(yield first_key_internal, first_value_internal)).new(size)
# ...
end
private def first_key_internal
i = 0
keys[i]
end
private def first_value_internal
i = 0
values[i]
end
end |
this is expected to work: https://play.crystal-lang.org/#/r/1ghu
The text was updated successfully, but these errors were encountered: