# Is there a place for \`dry-dsl\`?

**URL:** <https://discourse.dry-rb.org/t/is-there-a-place-for-dry-dsl/775>\
**Category:** Uncategorized\
**Created:** [May 6, 2019, 5:37pm UTC](https://discourse.dry-rb.org/t/is-there-a-place-for-dry-dsl/775 "2019-05-06T17:37:39Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![waiting-for-dev](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/waiting-for-dev/32/71_2.png) [@waiting-for-dev](https://discourse.dry-rb.org/u/waiting-for-dev)\
**Post date:** [May 6, 2019, 5:37pm UTC](https://discourse.dry-rb.org/t/is-there-a-place-for-dry-dsl/775/1 "2019-05-06T17:37:39Z")

</div>

Developing [the library I announced recently](https://discourse.dry-rb.org/t/introduing-a-web-pipe-for-the-dry-ecosystem/765), I took inspiration from `dry-transaction` in order to design and implement its DSL layer. I started using the very same implementation in `dry-transaction` and refactored it further until I got a clear model and understanding of what I was doing.

It can be summarized with the following pattern generalized for a library of name `A`:

- `include A` just delegates to including `A.()`, which in turns delegates to including an `A::Builder` instance.

- Consequently, a way to pass extra arguments is to directly `include A.(args)`, which will pipe the arguments to the `A::Builder#initialize` method.

- The instance of `A::Builder` will do two things to the host class:

- `A::ClassContext` instance will define the state of the host class, which comes from two different sources:

- `A::InstanceMethods` will define an initializer which, along with any other needed processing, will take all the state defined at the class level (which as I said comes from `A::ClassContext`) and copy it as its own instance state.

- `A` will become usable by initializing it with any required argument and then calling any method defined in `A::InstanceMethods`.

I won’t blame you I you haven’t read until here 😄 Anyway, you can see an example in my library:

- [Base module](https://github.com/waiting-for-dev/web_pipe/blob/master/lib/web_pipe.rb)
- [DSL machinery](https://github.com/waiting-for-dev/web_pipe/tree/master/lib/web_pipe/dsl).

What I want to re-mark here is that maybe here we have a common pattern which can be reused. The only variables which would change from library A to library B are:

- State defined as arguments of `Module.()`.
- State defined in `Module::DSLContext`.
- Extra arguments and state processing of `Module::InstanceMethods#initialize`.
- Extra methods defined in `Module::InstanceMethods`

So, this message is to gather other views on this. Do you think that this model would encompass all the needs that arise when developing a DSL or, at least, be a good starting point which could be easily extended? Or am I being short-sighted here focusing only in the use case I faced? Do you think it could implement DSL’s used across `dry-rb` libraries? It would be a good thing in order to be consistent across libraries (right now they differ: `include Dry.Types()`, `include Dry::Transaction`, `extend Dry::Initializer`… maybe even `< Dry::Struct`?). It is not something I could work on right now, but it could be an idea for the near future…

---

<div class="post-metadata">

**Author:** ![waiting-for-dev](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/waiting-for-dev/32/71_2.png) [@waiting-for-dev](https://discourse.dry-rb.org/u/waiting-for-dev)\
**Post date:** [May 6, 2019, 5:38pm UTC](https://discourse.dry-rb.org/t/is-there-a-place-for-dry-dsl/775/2 "2019-05-06T17:38:35Z")

</div>

Another nice side effect would be to help decoupling functionality from DSL.

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [May 8, 2019, 6:37pm UTC](https://discourse.dry-rb.org/t/is-there-a-place-for-dry-dsl/775/3 "2019-05-08T18:37:43Z")

</div>

I thought about this in the past but eventually decided it’s not worth the effort. Unifying DSL implementations would be a big effort with unknown benefits. It would also introduce a dependency for _most_ projects, and even if we put it into `dry-core` having such a dependency would make upgrades harder. I also think it would be much more difficult than it may seem to be.

I’m not saying “absolutely no” though, it’s something to keep in mind and consider in the future (from my pov at least).

> [@waiting-for-dev](#):
>
> It would be a good thing in order to be consistent across libraries (right now they differ: `include Dry.Types()` , `include Dry::Transaction` , `extend Dry::Initializer` … maybe even `< Dry::Struct` ?).

I can see how `Transaction` vs `Initializer` are inconsistent, so we could address this at some point. When it comes to `Dry::Types()` though - it is pretty much consistent with other module-builders like `Dry::Equalizer(:foo, :bar)` etc.

---

<div class="post-metadata">

**Author:** ![waiting-for-dev](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/waiting-for-dev/32/71_2.png) [@waiting-for-dev](https://discourse.dry-rb.org/u/waiting-for-dev)\
**Post date:** [May 9, 2019, 6:04am UTC](https://discourse.dry-rb.org/t/is-there-a-place-for-dry-dsl/775/4 "2019-05-09T06:04:54Z")

</div>

Thanks for your answer @solnic. I mainly agree with you:

> Unifying DSL implementations would be a big effort with unknown benefits.

The DSL libary seems very easy to be implemented but, as you say, refactoring all the other libraries would be a lot of work. But I think it would give a very nice benefit: decoupling core functionality from DSL. For example, [you can use the library I developed without the DSL](https://github.com/waiting-for-dev/web_pipe#standalone-usage). Surely very few people will, but this decoupling makes the code easier to refactor and evolve.

> It would also introduce a dependency for _most_ projects, and even if we put it into `dry-core` having such a dependency would make upgrades harder.

Yep, it would add a dependency but, as always, we should study whether it pays off or not. In my view, if a DSL abstraction makes sense and it works for all the scenarios, it definitely pays off. It is the same case that `dry-configurable`: it is a dependency, but it covers a very specific need that, otherwise, you end up programming one time and another. Being `dry-rb` a collection of libraries with function-objects in mind, `dry-dsl` would be the perfect place where all the state handling is encapsulated. All the other libraries could be 100% state-free (except for injection on initialization, which is the benefit of function-objects). But my big hesitation is whether a library like that can really encompass all use cases in an abstraction that makes sense.

> I also think it would be much more difficult than it may seem to be.

Yep, I’m also very afraid of that 🙂

> I’m not saying “absolutely no” though, it’s something to keep in mind and consider in the future (from my pov at least).

Yep, and I’m not saying “absolutely yes” 🙂 It deserves some playground sessions…

> When it comes to `Dry::Types()` though - it is pretty much consistent with other module-builders like `Dry::Equalizer(:foo, :bar)` etc.

But in this case it is `Dry.Types`, notice `.` vs `::`.

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [May 9, 2019, 7:18am UTC](https://discourse.dry-rb.org/t/is-there-a-place-for-dry-dsl/775/5 "2019-05-09T07:18:29Z")

</div>

I would prefer to wait with this type of efforts until all libs are at 1.0.0. To me this seems to be a very low priority for now.

> [@waiting-for-dev](#):
>
> But in this case it is `Dry.Types` , notice `.` vs `::` .

Right, we should decide which syntax we prefer and use it consistently (`Dry::Types()` and `Dry.Types()` do the same thing).
