Skip to content
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

Fix key type for empty NamedTuple be Symbol #10942

Merged

Conversation

caspiano
Copy link
Contributor

This PR changes the semantics of to_a and to_h on an empty NamedTuple and includes documentation of said behaviour.

After this PR, the following behaviour is expected...

t = NamedTuple.new
typeof(t.to_h) # => Hash(Symbol, NoReturn)
typeof(t.to_a) # => Array(Tuple(Symbol, NoReturn))

Resolves #9923
Resolves #10939

src/named_tuple.cr Outdated Show resolved Hide resolved
Copy link
Member

@straight-shoota straight-shoota left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is technically breaking, but I'd consider it a bug fix. And it really shouldn't have a noteworthy negative impact.

spec/std/named_tuple_spec.cr Outdated Show resolved Hide resolved
src/named_tuple.cr Outdated Show resolved Hide resolved
@straight-shoota
Copy link
Member

Btw. is there any reason why #to_a fills the collection manually whereas #to_h constructs a literal?

@straight-shoota straight-shoota added kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:stdlib:collection labels Jul 14, 2021
@caspiano
Copy link
Contributor Author

caspiano commented Jul 14, 2021

No idea, @straight-shoota, I just made a minor change. I can change it to construct a literal?

Edit: Done ✅

Copy link
Member

@beta-ziliani beta-ziliani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @caspiano !

@beta-ziliani beta-ziliani added this to the 1.2.0 milestone Jul 14, 2021
@straight-shoota
Copy link
Member

straight-shoota commented Jul 14, 2021

I was actually just asking about literal vs. construcing in general, not specifically asking to replace one by the other. 🙈 I'd like to explicitly consider which one's actually preferable.

@caspiano
Copy link
Contributor Author

caspiano commented Jul 14, 2021

@straight-shoota I ran some benchmarks, looks like the literal approach is faster.

require "benchmark"

struct NamedTuple
  def literal_to_a
    {% if T.size == 0 %}
      [] of {Symbol, NoReturn}
    {% else %}
      [
        {% for key in T %}
          { {{key.symbolize}}, self[{{key.symbolize}}] },
        {% end %}
      ]
    {% end %}
  end

  def dynamic_to_h
    hash = Hash(typeof(first_key_internal), typeof(first_value_internal)).new(size)
    each do |key, value|
      hash[key] = value.as(typeof(first_value_internal))
    end
    hash
  end
end

test = {
  a: 1,
  b: "1",
  c: Time.unix(1),
  d: [1]
}

puts "NamedTuple.to_a"
Benchmark.ips do |x|
  x.report("literal") { test.literal_to_a }
  x.report("each") { test.to_a }
end

puts "NamedTuple.to_h"
Benchmark.ips do |x|
  x.report("literal") { test.to_h }
  x.report("each") { test.dynamic_to_h }
end
$ crystal run --release bench.cr
NamedTuple.to_a
literal  13.48M ( 74.19ns) (± 7.33%)  208B/op        fastest
   each  11.68M ( 85.61ns) (± 7.13%)  208B/op   1.15× slower
NamedTuple.to_h
literal   8.48M (117.94ns) (± 7.31%)  240B/op        fastest
   each   7.38M (135.47ns) (± 8.14%)  256B/op   1.15× slower

@HertzDevil
Copy link
Contributor

first_key_internal should be unused after this PR now that #10950 is also merged.

@straight-shoota straight-shoota changed the title Symbol instead of NoReturn as key type for empty NamedTuple Fix key type for empty NamedTuple be Symbol Jul 27, 2021
@straight-shoota straight-shoota merged commit 362c730 into crystal-lang:master Jul 27, 2021
@caspiano caspiano deleted the fix/empty-named-tuple-to-h branch July 27, 2021 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:stdlib:collection
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make NamedTuple.new.to_h return {} of Symbol => NoReturn Undocumented behavior of NamedTuple#to_h
4 participants