Extracting URI::Params is a struct? Why? - #10 by Blacksmoke16 to its own topic.
Using decorators is usually a better approach when you want to customize/extend a type, especially for ones you do not own. For example:
struct CustomRequest getter request : HTTP::Request forward_missing_to @request def self.new(method : String, path : String, headers : HTTP::Headers? = nil, body : String | Bytes | IO | Nil = nil, version : String = "HTTP/1.1") : self new HTTP::Request.new method, path, headers, body, version end def initialize(@request : HTTP::Request); end def safe? : Bool @request.method.in? "GET", "HEAD", "OPTIONS", "TRACE" end end
This way you can do something before/after a specific method, or add more methods, all the while not touching anything related to the upstream type itself.
HOWEVER, with the way the stdlib is implemented at the moment, this doesn’t work as well as it could since everything is typed as concrete implementations instead of abstractions. This means you cannot have this custom request type work as a drop in replacement to
HTTP::Request , even if it is 100% compatible with it.
PHP handles this via PHP Standards Recommendations - PHP-FIG, which define interfaces/requirements for various common libraries/implementations, such as eventing, caching, and HTTP clients, factories, and messages. This allows framework/application developers to design things such that any implementation could be used. E.g. an HTTP Client from Symfony, Guzzle, or even a custom implementation.
Ultimately this makes the user happier given they can use what they want and makes the developer’s life easier as they do not need to make a bunch of adapters or try and make things work with an arbitrary amount of implementations.
PHP’s package manager (composer) also taps into this feature via its provide key, which essentially allows defining a dependency like
psr/http-client-implementation that would be satisfied if a library that is installed “provides” that package. E.g. one from the list of Packagist.
I propose to make the stdlib less coupled with its concrete implementations by doing something similar to PHP’s PSR by introducing some interfaces (modules) that describe the public API that some common types must implement. This will allow using decorators as a drop in replacement, assuming they correctly implement the interface. E.g.
There are a few choices in regards to the exact implementation, which can be hashed out later.
- Stdlib better follows the
- Provides a standard for shard creators and consumers
- Provides a place to define/document requirements/suggestions for implementers, such as that HTTP headers MUST be case-insensitive
- Overhead of determining the API and updating all usages in stdlib
- How to distribute/provide the interfaces
- Possibly somewhat breaking given not every method on
HTTP::Requestwould be on the interface most likely
- Not everyone may be familiar with SOLID, interfaces, or benefits thereof