# \[RFC\] Standardized Abstractions

**URL:** <https://forum.crystal-lang.org/t/rfc-standardized-abstractions/4414>\
**Category:** Crystal Contrib\
**Created:** [February 21, 2022, 8:40pm UTC](https://forum.crystal-lang.org/t/rfc-standardized-abstractions/4414 "2022-02-21T20:40:15Z")\
**Posts on this page:** 1\
**Showing post:** 1

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [February 21, 2022, 8:40pm UTC](https://forum.crystal-lang.org/t/rfc-standardized-abstractions/4414/1 "2022-02-21T20:40:15Z")

</div>

# Standardized Abstractions

Extracting [URI::Params is a struct? Why? - #10 by Blacksmoke16](https://forum.crystal-lang.org/t/uri-params-is-a-struct-why/4344/10) to its own topic.

## Background

Using decorators is usually a better approach when you want to customize/extend a type, especially for ones you do not own. For example:

```auto
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’s PSR

PHP handles this via [PHP Standards Recommendations - PHP-FIG](https://www.php-fig.org/psr/), 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.

### Composer’s `provide`

PHP’s package manager (composer) also taps into this feature via its [provide](https://getcomposer.org/doc/04-schema.md#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](https://packagist.org/providers/psr/http-client-implementation).

## Proposal

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. `include RequestInterface`.

There are a few choices in regards to the exact implementation, which can be hashed out later.

### Benefits

- Stdlib better follows the `SOLID` principles
- Provides a standard for shard creators and consumers
  - I.e. imagine using [crest](https://github.com/mamantoha/crest) or [halite](https://github.com/icyleaf/halite) interchangeably with `HTTP::Client`. Such as within `OAuth2::AccessToken#authenticate` among other places

- Provides a place to define/document requirements/suggestions for implementers, such as that HTTP headers _MUST_ be case-insensitive

### Challenges

- 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::Request` would be on the interface most likely
- Not everyone may be familiar with SOLID, interfaces, or benefits thereof

---

_[View the full topic](https://forum.crystal-lang.org/t/rfc-standardized-abstractions/4414)._
