literal icon indicating copy to clipboard operation
literal copied to clipboard

Literal::Data is not behaving in a hash-like way

Open rachelgraves opened this issue 10 months ago • 7 comments

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.

rachelgraves avatar May 27 '25 09:05 rachelgraves

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)

stevegeek avatar May 27 '25 09:05 stevegeek

Yeah, I think the docs are wrong. The problem with implementing [] and []= is it would conflict with the visibility of readers and writers. 🤔

joeldrapper avatar May 27 '25 09:05 joeldrapper

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 avatar May 27 '25 09:05 joeldrapper

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.

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.

rachelgraves avatar May 27 '25 09:05 rachelgraves

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.

stevegeek avatar May 27 '25 10:05 stevegeek

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)) ?)

stevegeek avatar May 27 '25 10:05 stevegeek

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.

joeldrapper avatar May 27 '25 10:05 joeldrapper