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

Document how sum types works with structs #159

Open
solnic opened this issue May 22, 2020 · 4 comments
Open

Document how sum types works with structs #159

solnic opened this issue May 22, 2020 · 4 comments

Comments

@solnic
Copy link
Member

solnic commented May 22, 2020

refs #158

@johanlunds
Copy link

johanlunds commented Oct 26, 2021

For what it's worth, this was very unexpected behavior to me:

class A < Dry::Struct
  attribute :id,    Types::Strict::Integer
  attribute :type,  Types::Strict::String
  attribute :name,  Types::Strict::String
end

class B < Dry::Struct
  attribute :id,               Types::Strict::Integer
  attribute :type,             Types::Strict::String
  attribute :name,             Types::Strict::String
  attribute :primary_function, Types::Strict::String.optional
end

class WithSum < Dry::Struct
  attribute :sum, A | B
end

WithSum.new(
  sum: B.new(
    id: 123, 
    type: 'foo', 
    name: 'bar', 
    primary_function: 'yo'
  )
) # => #<WithSum sum=#<A id=123 type="foo" name="bar">>

I expected it to return my instance of B, not A.

A nice first step, would be to at least document how sum types work.

But I wonder if it's intended to work this way too.

@flash-gordon
Copy link
Member

But I wonder if it's intended to work this way too.

The problem lies in implicit coercion with to_hash. B gets coerced to a hash and to an A in order. It wasn't a good design choice. You can reject extra key by making the schema strict with schema schema.strict:

class StrictStruct < Dry::Struct
  schema schema.strict
end

class A < StrictStruct
  attribute :id,    Types::Strict::Integer
  attribute :type,  Types::Strict::String
  attribute :name,  Types::Strict::String
end

class B < StrictStruct
  attribute :id,               Types::Strict::Integer
  attribute :type,             Types::Strict::String
  attribute :name,             Types::Strict::String
  attribute :primary_function, Types::Strict::String.optional
end

class WithSum < Dry::Struct
  attribute :sum, A | B
end

This will still coerce data internally but these attempts will fail. I think we'll remove to_hash in 2.0.

@johanlunds
Copy link

johanlunds commented Oct 26, 2021

Thank you for the context @flash-gordon 🙏.

And thanks for the suggestion. I solved our case with constrained types:

Types::Strict::String.constrained(eql: 'my_type')

@flash-gordon
Copy link
Member

Yeah, I do something similar to discriminate variants. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants