# \[RFC\] Let \`include\`, \`extend\` and \`require\` be macros

**URL:** <https://forum.crystal-lang.org/t/rfc-let-include-extend-and-require-be-macros/4422>\
**Category:** Crystal Contrib\
**Created:** [February 24, 2022, 1:06pm UTC](https://forum.crystal-lang.org/t/rfc-let-include-extend-and-require-be-macros/4422 "2022-02-24T13:06:26Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [February 24, 2022, 1:06pm UTC](https://forum.crystal-lang.org/t/rfc-let-include-extend-and-require-be-macros/4422/1 "2022-02-24T13:06:26Z")

</div>

Some time ago I realized that we really don’t need `include`, `extend` and `require` be keywords. They can be macros defined at the top-level, marked with `@[Primitive]` and handled specially in the compiler.

The end result is not too much. But we get these things:

- Parsing is slightly easier because we don’t need to consider these cases
- They will appear in API docs and we can document them nicely, in addition to documenting them in the Reference guide
- One could define an `include` or `require` method in a class and use that without `self.` and it will work. For example, a spec library like Spectator could define an `include` method so one can do `expect(subject).to include(something)` (right now that’s not possible)

I played a bit with the compiler and this seems to be really easy to implement.

Do you think we should do it?

This thought is slightly inspired by [this blog post I wrote](https://dev.to/asterite/why-i-love-ruby-part-3-2oin).

---

<div class="post-metadata">

**Author:** ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)\
**Post date:** [February 24, 2022, 1:48pm UTC](https://forum.crystal-lang.org/t/rfc-let-include-extend-and-require-be-macros/4422/2 "2022-02-24T13:48:18Z")

</div>

Sounds brilliant! 🤟

However, we’d need to take care about calling these macros from locations where they’re currently invalid, such as `{% require "foo" %}`. I could see some use case from calling `require` from a macro context if you want to load the source of that file into a macro variable. So it would probably be fine if it worked. But then it creates problems with resolving multiple require calls to the same file. So we probably need to keep some restrictions in place to make sure code from a required file ends up in the program.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [February 24, 2022, 2:56pm UTC](https://forum.crystal-lang.org/t/rfc-let-include-extend-and-require-be-macros/4422/3 "2022-02-24T14:56:15Z")

</div>

Oh, I don’t think `{% require "foo" %}` will work.

The way it will work is like this:

1. You write `require "foo"`
2. The compiler finds a `macro require(path)` at the top-level and tries to expand it
3. The compiler finds it: it’s a primitive! The primitive is handled like this: expand the macro to the `Require` AST node we currently have
4. That `Require` node is processed as usual

So the implementation is just “magic” that defaults to the current implementation.

Does that make sense?

---

<div class="post-metadata">

**Author:** ![naqvis](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/naqvis/32/906_2.png) [@naqvis](https://forum.crystal-lang.org/u/naqvis)\
**Post date:** [February 24, 2022, 3:29pm UTC](https://forum.crystal-lang.org/t/rfc-let-include-extend-and-require-be-macros/4422/4 "2022-02-24T15:29:05Z")

</div>

Quick question: Will changing `include` to macro going to change existing semantics? For example now if we `include` some `Module Foo` and when we invoke `obj.is_a?(Foo)` this returns `true`. Will this behavior remain same after the change?

```crystal
module Foo
  def foo
    "foo"
  end
end

class Bar
  include Foo
end

b = Bar.new
pp b.is_a?(Foo) # => true

```

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [February 24, 2022, 3:43pm UTC](https://forum.crystal-lang.org/t/rfc-let-include-extend-and-require-be-macros/4422/5 "2022-02-24T15:43:04Z")

</div>

Yes! Everything will remain the same. The only difference is that `include` will be listed in the docs, and it will be parsed like a regular call.

---

<div class="post-metadata">

**Author:** ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)\
**Post date:** [February 24, 2022, 7:13pm UTC](https://forum.crystal-lang.org/t/rfc-let-include-extend-and-require-be-macros/4422/6 "2022-02-24T19:13:29Z")

</div>

For the record: Adding these features to the API docs would work even without making them primitive macros. We could do the same as with pseudo methods (see [`src/docs_pseudo_methods.cr`](https://github.com/crystal-lang/crystal/blob/a978a06f306428c0172b96aee72609767dc7a5ca/src/docs_pseudo_methods.cr)).

So both changes are technically independent. But the core is a change in how we view them.

---

<div class="post-metadata">

**Author:** ![jzakiya](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/jzakiya/32/278_2.png) [@jzakiya](https://forum.crystal-lang.org/u/jzakiya)\
**Post date:** [February 24, 2022, 7:38pm UTC](https://forum.crystal-lang.org/t/rfc-let-include-extend-and-require-be-macros/4422/7 "2022-02-24T19:38:14Z")

</div>

> [@asterite](#):
>
> This thought is slightly inspired by [this blog post I wrote](https://dev.to/asterite/why-i-love-ruby-part-3-2oin).

Insidiously brilliant in enticing `Rubyist` to investigate `Crystal`! 😉  
Heck, even made me want to learn `Crystal`. 😄  
Also mentioned in this weeks Ruby Weekly.

[https://rubyweekly.com/issues/592](https://rubyweekly.com/issues/592)
