Literal::Data is not behaving in a hash-like way
According to the docs Literal::Data should behave in a hash-like way, however when I do the following it tells me [] is an undefined method.
Both are hash-like, meaning they can be used in place of a hash in some cases. You can access their properties using the [] method. Literal::Struct also supports []= to set properties.
Running v1.8
Sample code:
# frozen_string_literal: true
class ChartPoint < Literal::Data
prop :x, String
prop :y, _Union(Integer, Float)
end
Testing:
[1] pry(main)> f = ChartPoint.new(x: "foo", y: 12.1)
=> #<ChartPoint:0x00000001251bef10 @x="foo", @y=12.1>
[2] pry(main)> f[:x]
NoMethodError: undefined method `[]' for an instance of ChartPoint
Expected outcome:
[1] pry(main)> f = ChartPoint.new(x: "foo", y: 12.1)
=> #<ChartPoint:0x00000001251bef10 @x="foo", @y=12.1>
[2] pry(main)> f[:x]
=> "foo"
Literal::Struct
Works as expected.
Hey @rachelgraves , I believe the docs are wrong then, as Literal::Data was meant to be an analog of Data which does not implement #[] either.
irb(main):001> Test = Data.define(:x)
=> Test
irb(main):002> Test.new(x: 1).x
=> 1
irb(main):003> Test.new(x: 1)[:x]
(irb):3:in '<main>': undefined method '[]' for an instance of Test (NoMethodError)
Yeah, I think the docs are wrong. The problem with implementing [] and []= is it would conflict with the visibility of readers and writers. 🤔
Actually, I do think it would be reasonable for these classes to implement the hash methods as described in the docs. They already implement things like pattern matching.
Hey @rachelgraves , I believe the docs are wrong then, as
Literal::Datawas meant to be an analog ofDatawhich does not implement#[]either.
Oh interesting, I guess what you say makes sense, although I do dislike that picking the immutable option also chops off [] too.
Yeah, I think the docs are wrong. The problem with implementing [] and []= is it would conflict with the visibility of readers and writers. 🤔 Actually, I do think it would be reasonable for these classes to implement the hash methods as described in the docs. They already implement things like pattern matching.
@joeldrapper I don't think you'd want to implement []= since Data is meant to be immutable, I wonder why they (the people who implemented Data) don't let you use [] though.
The Data feature spec does touch on this I think, https://bugs.ruby-lang.org/issues/16122 . I think the point was to sort of separate Data as a value representation that does not also imply "collection" like behaviour
Having [] and each defined on something that is thought as "just value" can also lead to subtle bugs, when some method checks "if the received argument is collection-alike", and value object's author doesn't thought of it as a collection.
Of course, not implying we have to follow those semantics! Just considering one option is to stay close to Data, or even to try make Literal::Data more of a drop in replacement for Data (eg add a Literal::Data.define , eg ChartPoint = Literal::Data.define(x: String, y: _Union(Integer, Float)) ?)
I’m personally not too concerned about sticking to Ruby’s Data and Struct interfaces. The purpose of these objects is to provide useful interfaces to structured data — both mutable and immutable — with sensible defaults. If you want fine-grained control without any defaults Literal::Object provides that.