Code Monkey home page Code Monkey logo

Comments (7)

greghuc avatar greghuc commented on June 2, 2024 2

My subclassing suggestion wasn't really about custom classes, and more about the constrained semantics of a (primitive) value being fully communicated to the rest of a system's code via the type system (using a subclass).

dry-types Struct/Value classes look useful for representing composite data types of multiple attributes. The examples on the Struct & Value docs page are all composite data types. I've just created my first Struct class to represent a Message composed of an 'email' string and a 'content' string.

But if I'm understanding this correctly, a constrained-type represents a single primitive value. And the constraints (in the form of default, enum, format, max_size, etc) ensure that the value can only be drawn from a subset of the wider primitive type. But the explicitly-constrained nature of this value is not manifestly visible in the type system if Strict::String.primitive == String. That's why I suggested the subclassing idea.

For example, if we had a LowercaseString constrained-type that lowercases all inputs, then it seems questionable that a LowercaseString value just has class String, not LowercaseString < String. Without additional information, anyone consuming such a value would think that "HeLLO" was a possible value when it's not; only "hello" is.

The reason I suggested the subclassing idea is drawn from a concrete example. I'm refactoring a JSON-api webapp, and want to separate input-validation (using dry-validation) from domain object construction (using dry-types). Once the inputs have been schema-validated and coercively typed, they should be in their final/immutable form; the input hash can then be used to construct a dry-types Struct domain object. Ideally I would like:

  • Input-validation coerces input attributes into a hash of primitive types and constrained primitive types, with complete type information (e.g. LowercaseString).
  • The Struct Domain object constructor consumes this hash, and uses the correctly typed attributes as is. There is no need to reconstruct them, as the attributes already have the desired types.

From debugging my code, this isn't happening right now. In the LowercaseString constrained-type example:

  • During input validation, with a schema declaring 'word' attribute of LowercaseString: { "word": "HELLO" } becomes { :word: "hello" } (with type String).
  • During domain object construction, with a Struct declaration wanting the same 'word' attribute of LowercaseString: the already-validated hash of { :word: "hello" } is regenerated again as { :word: "hello" } (type String).

I find this lacking in two ways, which would be remedied by the subclassing idea (or something like it):

  • Domain object construction has to reconstruct 'hello' attribute all over again using the LowercaseString constrained-type. This would be unnecessary if the attribute was declared of type LowercaseString; it would already pass type?(LowercaseString)
  • Any code consuming the domain_object.word has no indication (through the type system) that the attribute is not just a String, but a lowercase String.

My two cents, and thanks for such a cool library :-)

from dry-types.

AMHOL avatar AMHOL commented on June 2, 2024

You can do this in your constructor:

require 'bundler/inline'

gemfile(true) { gem 'dry-types', github: 'dry-rb/dry-types' }

class EmailString < String
  alias to_s dup
end

module Types
  include Dry::Types.module

  # Constrained email type: a string of certain form and size, and normalised
  EmailString = Strict::String.constrained(
      format: /\A[^\s@]+@([^\s@.]+\.)+([^\s@.]+)\z/u, # Must loosely look like email
      max_size: 254 # Maximum possible length of email
  ).constructor { |value|
    value.kind_of?(::String) ? ::EmailString.new(value.strip.downcase) : value
  }
end

value = Types::EmailString['   [email protected] ']
# => "[email protected]"
value
# => "[email protected]"
value.class
# => EmailString

from dry-types.

greghuc avatar greghuc commented on June 2, 2024

Thanks - that's both cool and hacky!

The constructed value now has type EmailString subclassing String. That's great.

But it does feel weird that there's both an EmailString class, and a Types::EmailString 'pseudo-class'.

from dry-types.

solnic avatar solnic commented on June 2, 2024

We make a distinction between a dry-type and its primitive class, so Strict::String.primitive == String etc. dry types are pretty much object constructors with optional constraints and possible additional behaviors (like default value, enum etc.). If you want custom classes you can use Struct or Value.

from dry-types.

flash-gordon avatar flash-gordon commented on June 2, 2024

@greghuc this is a very interesting suggestion, we could make some use of it. It's unlikely we'll something like this in 1.0 but this way of tagging values via subtyping can be fruitful in future. This may be tricky a bit now since we have AST representation of types but possible nevertheless. Thanks for the insight, I'll close this one because I'm grooming the backlog, further discussions can be continued on the discussion forum.

from dry-types.

sadhu89 avatar sadhu89 commented on June 2, 2024

I think this feature would be great. Just wondering if there has been any further discussion about it since 2018. I would love to contribute to get it done!

from dry-types.

solnic avatar solnic commented on June 2, 2024

@sadhu89 hey there was no progress because there was no follow up discussion on the forum. Feel free to re-start this thread on the forum and we can take it from there.

from dry-types.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.