Comments (7)
Oh boy, now I remember it's not the first time when I'm getting confused by this :( Maybe we should improve the error message? Like "input violates constraints of all the possible types, last error: blah blah"?
from dry-types.
Thanks for this detailed report, this is clearly a bug in error handling logic in Sum
type although I can't tell why it's happening after having a quick look at Sum#try 😕
from dry-types.
Sum returns the last error, this was always the case. It tries the left type first, then thee right one. It also bothers me but I'm not sure this can be improved. We could probably collect all failures but this will affect performance. Also, message size can be really large for complex types.
from dry-types.
FWIW I don't think it's a bug.
from dry-types.
Yeah, it would definitely make sense.
from dry-types.
Hi, all! First of all, just want to thank you guys for the amazing library ❤️
I've faced the same issue that a sum type raises only the error of the last type's constructor, which is really confusing.
After looking into the implementation details I think the problem with returning an error that gives a more comprehensive message is that it needs to somehow pass some state from all the left types to the right ones step by step because a final sum type it's only a sum of a complex left and a simple right. Is that right?
What do you think about adding a union type that is going to store all the types at the same level?
Here is what I mean
class Union < Dry::Types::Union
possible_types(
Dry::Types::String,
Dry::Types::Integer,
# and etc
)
end
One additional benefit that we could get from adding a union type is explicit control over what type an input should be assigned.
Here is an example:
class A < Dry::Struct
attribute :value, Types::String
end
class B < Dry::Struct
attribute :value, Types::String
end
Data = A | B
hash = { value: 'a' }
p Data[hash].class # => It is always going to be the first left type in a sum.
With a union type, it would be possible to override the default behavior, which is finding a first type whose constructor succeeds.
class Data < Dry::Types::Union
possible_types A, B
class << self
def resolve(data)
case data
in { value: 'a' }
A
in { value: 'b' }
B
end
end
end
end
from dry-types.
Here is a draft I wrote and which worked out in my case:
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'dry-types'
gem 'dry-struct'
end
module Types
include Dry.Types()
end
module Dry
module Types
class Union
include Builder
include Meta
attr_reader :types
Error = Class.new(StandardError)
def initialize(*types)
raise ArgumentError, 'a union should consist of at least 2 types' if types.size < 2
@types = types
@meta = {}
end
def try(input)
type = resolve(input)
return Dry::Types::Result::Success.new(input) if type
error = Error.new("A value does not match any type of union")
failure = Dry::Types::Result::Failure.new(input, error)
yield failure if block_given?
failure
end
def [](input)
call_unsafe(input)
end
def call_unsafe(input)
type = resolve(input)
raise Error, "value #{input} does not match any type of union" if type.nil?
type.call_unsafe(input)
end
def constrained?
false
end
private
def resolve(input)
find_type(input)
end
def find_type(input)
types.find { |type| type.valid?(input) }
end
class << self
def of(*types, &block)
new(*types).tap do |instance|
instance.instance_exec(&block) if block_given?
end
end
end
end
end
end
class A < Dry::Struct
attribute :value, Types::String
end
class B < Dry::Struct
attribute :value, Types::String
end
Data = Dry::Types::Union.of(A, B) do
def resolve(input)
case input
in { value: 'a' } if A.valid?(input)
A
in { value: 'b' } if B.valid?(input)
B
else
find_type(input)
end
end
end
p Data[{ value: 'a' }].class
p Data[{ value: 'b' }].class
p Data[{ }] # => Error
I haven't really dived into all the details of what interfaces the type should implement so my point here is to just make a suggestion.
I would really appreciate hearing your opinions on that @solnic @flash-gordon
from dry-types.
Related Issues (20)
- Include `Types::Params::String` HOT 8
- Type#fallback to mock previous implementation of Type#default HOT 10
- Constructor is not applied properly to the result of types Sum HOT 5
- Types::Params::Integer incorrect coercion for numeric values HOT 9
- Types::Params::*.optional not handling empty string HOT 4
- Anonymous Dry::Types based module cannot be extended HOT 4
- Params namespace disapearing HOT 9
- Params coercion for Array works in an unintuitive way HOT 1
- https://dry-rb.org/gems/dry-types/ not at latest version HOT 1
- [Security] Workflow ci.yml is using vulnerable action actions/checkout
- Compound Types with Examples HOT 1
- Dry::Container::Error: There is already an item registered with the key "nominal.string" on jruby-head HOT 4
- Future-proof local override for Types::Instance/Constructor classes that potentially collide with predicates
- Types::Coeercible::Integer, should this use Base 10? HOT 2
- Add Zeitwerk autoloader
- Using transform_keys breaks error handling
- Undefined method after upgrading dependencies. HOT 5
- Missing method implementation for `Enum.each_value` HOT 1
- Types.Constructor failing on Ruby 3.1 HOT 8
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dry-types.