Issue #16986 has been updated by duerst (Martin D=FCrst). mame (Yusuke Endoh) wrote in #note-38: > So, external input like JSON data is not the target of this proposal. Ra= ther, this proposal is just a variant of Struct, which allows to omit the d= efinition line: `Foo =3D Struct.new(...)`. I'm still struggling to understand the actual uses of this proposal. I unde= rstand that many people like it, but can we see *actual real use cases*, i.= e. code that not only gets shorter but also continues to be at least as eas= y to understand as before? I'm trying to compare these "anonymous Structs" to anonymous functions. The= success of anonymous functions would suggest that "anonymous structures" a= re also a good idea. But I'm not so sure about this. For anonymous functions (blocks, lambdas), there's no need to discuss wheth= er two of them that look identical are identical or not; it's rather rare t= o have the same thing twice anyway (something like {|a,b| a+b} might be an = example that may turn up multiple times). Functions also don't have a secon= d level of instantiation with actual data. And there's not much of a questi= on about the semantics of such functions (in the above example, one could w= onder whether it's an addition or a concatenation, but duck typing mostly m= akes that unnecessary/impossible). For "anonymous Structs", the question of when two of these are the same see= ms to have several different expectations with different implementation and= runtime consequences, but no widely satisfying solution. But it doesn't se= em rare to have an "anonymous Struct" with the same components, because the= actual instance data can differ. But the more instances I have, the strong= er the need for a name becomes. If I have something like `${ name: 'foo', n= umber: 'abcde' }`, I'm starting to wonder what that is: A person in a compa= ny? A room with name and number? Anything else of a lot of different choice= s? So as you might guess, at this point, I'm very much not convinced. Examples= with `a: 1, b: 2` don't really count as actual use cases, and are not conc= rete enough to help understand the actual pros and cons (except for syntax,= which should be secondary). ---------------------------------------- Feature #16986: Anonymous Struct literal https://bugs.ruby-lang.org/issues/16986#change-87360 * Author: ko1 (Koichi Sasada) * Status: Open * Priority: Normal * Assignee: matz (Yukihiro Matsumoto) ---------------------------------------- # Abstract How about introducing anonymous Struct literal such as `${a: 1, b: 2}`? It is almost the same as `Struct.new(:a, :b).new(1, 2)`. # Proposal ## Background In many cases, people use hash objects to represent a set of values such as= `person =3D {name: "ko1", country: 'Japan'}` and access its values through= `person[:name]` and so on. It is not easy to write (three characters `[:]`= !), and it easily introduces misspelling (`person[:nama]` doesn't raise an = error). If we make a `Struct` object by doing `Person =3D Struct.new(:name, :age)` = and `person =3D Person.new('ko1', 'Japan')`, we can access its values throu= gh `person.name` naturally. However, it costs coding. And in some cases, we= don't want to name the class (such as `Person`). Using `OpenStruct` (`person =3D OpenStruct.new(name: "ko1", country: "Japan= ")`), we can access it through `person.name`, but we can extend the fields = unintentionally, and the performance is not good. Of course, we can define a class `Person` with attr_readers. But it takes s= everal lines. To summarize the needs: * Easy to write * Doesn't require declaring the class * Accessible through `person.name` format * Limited fields * Better performance ## Idea Introduce new literal syntax for an anonymous Struct such as: `${ a: 1, b: = 2 }`. Similar to Hash syntax (with labels), but with `$` prefix to distinguish. Anonymous structs which have the same member in the same order share their = class. ```ruby s1 =3D ${a: 1, b: 2, c: 3} s2 =3D ${a: 1, b: 2, c: 3} assert s1 =3D=3D s2 s3 =3D ${a: 1, c: 3, b: 2} s4 =3D ${d: 4} assert_equal false, s1 =3D=3D s3 assert_equal false, s1 =3D=3D s4 ``` ## Note Unlike Hash literal syntax, this proposal only allows `label: expr` notatio= n. No `${**h}` syntax. This is because if we allow to splat a Hash, it can be a vulnerability by s= platting outer-input Hash. Thanks to this spec, we can specify anonymous Struct classes at compile tim= e. We don't need to find or create Struct classes at runtime. ## Implementatation https://github.com/ruby/ruby/pull/3259 # Discussion ## Notation Matz said he thought about `{|a: 1, b: 2 |}` syntax. ## Performance Surprisingly, Hash is fast and Struct is slow. ```ruby Benchmark.driver do |r| r.prelude <<~PRELUDE st =3D Struct.new(:a, :b).new(1, 2) hs =3D {a: 1, b: 2} class C attr_reader :a, :b def initialize() =3D (@a =3D 1; @b =3D 2) end ob =3D C.new PRELUDE r.report "ob.a" r.report "hs[:a]" r.report "st.a" end __END__ Warming up -------------------------------------- ob.a 38.100M i/s - 38.142M times in 1.001101s (26.25= ns/i, 76clocks/i) hs[:a] 37.845M i/s - 38.037M times in 1.005051s (26.42= ns/i, 76clocks/i) st.a 33.348M i/s - 33.612M times in 1.007904s (29.99= ns/i, 87clocks/i) Calculating ------------------------------------- ob.a 87.917M i/s - 114.300M times in 1.300085s (11.37= ns/i, 33clocks/i) hs[:a] 85.504M i/s - 113.536M times in 1.327850s (11.70= ns/i, 33clocks/i) st.a 61.337M i/s - 100.045M times in 1.631064s (16.30= ns/i, 47clocks/i) Comparison: ob.a: 87917391.4 i/s hs[:a]: 85503703.6 i/s - 1.03x slower st.a: 61337463.3 i/s - 1.43x slower ``` I believe we can speed up `Struct` similarly to ivar accesses, so we can im= prove the performance. BTW, OpenStruct (os.a) is slow. ``` Comparison: hs[:a]: 92835317.7 i/s ob.a: 85865849.5 i/s - 1.08x slower st.a: 53480417.5 i/s - 1.74x slower os.a: 12541267.7 i/s - 7.40x slower ``` For memory consumption, `Struct` is more lightweight because we don't need = to keep the key names. ## Naming If we name an anonymous class, literals with the same members share the nam= e. ```ruby s1 =3D ${a:1} s2 =3D ${a:2} p [s1, s2] #=3D> [#<struct a=3D1>, #<struct a=3D2>] A =3D s1.class p [s1, s2] #=3D> [#<struct A a=3D1>, #<struct A a=3D2>] ``` Maybe that is not a good behavior. -- = https://bugs.ruby-lang.org/ Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=3Dunsubscribe> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>