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 example in Hash#from #7210

Merged
merged 2 commits into from
Dec 28, 2018
Merged

Fix example in Hash#from #7210

merged 2 commits into from
Dec 28, 2018

Conversation

wooster0
Copy link
Contributor

This is the current code:

require "json"

def speak_about(thing : String, n : Int64)
  "I see #{n} #{thing}s"
end

data = JSON.parse(%({"thing": "world", "n": 2})).as_h
speak_about(**{thing: String, n: Int64}.from(data)) # => "I see 2 worlds"

which results in:

Error in line 8: instantiating 'NamedTuple(thing: String.class, n: Int64.class)#from(Hash(String, JSON::Any))'

in /usr/lib/crystal/named_tuple.cr:77: expanding macro

    {% begin %}
    ^

in macro 'macro_94851401795888' /usr/lib/crystal/named_tuple.cr:77, line 4:

   1. 
   2.       NamedTuple.new(
   3.       
>  4.         "thing": self[:thing].cast(hash.fetch(:thing) { hash["thing"] }),
   5.       
   6.         "n": self[:n].cast(hash.fetch(:n) { hash["n"] }),
   7.       
   8.       )
   9.     

instantiating 'String.class#cast(JSON::Any)'
in /usr/lib/crystal/class.cr:124: can't cast JSON::Any to String

    other.as(self)
    ^

Because every value of data is JSON::Any.

Now it's:

require "json"

def speak_about(thing : String, n : Int64)
  "I see #{n} #{thing}s"
end

hash = JSON.parse(%({"thing": "world", "n": 2})).as_h

# Cast types appropriately:
data = {} of String => JSON::Any::Type
hash.each { |key, value| data[key] = value.raw }

speak_about(**{thing: String, n: Int64}.from(data)) # => "I see 2 worlds"

which appropriately casts every value to the real, raw type.

Copy link
Member

@bcardiff bcardiff left a comment

Choose a reason for hiding this comment

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

Maybe having the following shorter snippet with type annotation might be useful for the user.

hash = JSON.parse(%({"thing": "world", "n": 2})).as_h # hash : Hash(String, JSON::Any)
data = hash.transform_values(&.raw)                   # data : Hash(String, String | Int64 | Array(JSON::Any) | ... )
speak_about(**{thing: String, n: Int64}.from(data))   # => "I see 2 worlds"

@bcardiff
Copy link
Member

The word appropriately might not be clear for the future reader. Showing the evolution of types would be more straight.

@asterite
Copy link
Member

I have no idea how this works:

{thing: String, n: Int64}.from(data)

It should be:

NamedTuple(thing: String, n: Int64).from(data)

@asterite
Copy link
Member

Oh, nevermind... it's a bit hacky, but well...

@bcardiff bcardiff merged commit 8d66192 into crystal-lang:master Dec 28, 2018
@bcardiff bcardiff added this to the 0.27.1 milestone Dec 28, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants