diff --git a/priv/blog/staged/2023/2023-05-11-There-are-macros-and-there-are-macros.md b/priv/blog/staged/2023/2023-05-11-There-are-macros-and-there-are-macros.md index db29f5f..ed1d698 100644 --- a/priv/blog/staged/2023/2023-05-11-There-are-macros-and-there-are-macros.md +++ b/priv/blog/staged/2023/2023-05-11-There-are-macros-and-there-are-macros.md @@ -12,7 +12,7 @@ tag_names: - "ash" - "macros" author: "Zach Daniel" -body_html: '

__ash_blog_newline_hack__ A common criticism of Ash is that it has too many macros. This is an understandable position to take, but I think its important to distinguish between two main kinds of macros. One of them should be used extremely sparingly, and the other I think its okay to use a bit more liberally. We’ll call them “configuration macros” and “metaprogramming macros”. Keep in mind these names are a bit made up and the boundary between the two is not always cut and dry. When people talk about “magic” in the context of macros, they are mostly talking about “metaprogramming macros”. Lets take a look at some examples.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ Metaprogramming Macros__ash_blog_newline_hack__ __ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ This snippet is from__ash_blog_newline_hack__ __ash_blog_newline_hack__ NX__ash_blog_newline_hack__ __ash_blog_newline_hack__ . This function can run as regular elixir code, or it can be compiled to run using multiple backends meant for numerical processing, including running on the GPU. The code that is actually compiled under the hood looks__ash_blog_newline_hack__ __ash_blog_newline_hack__ dramatically__ash_blog_newline_hack__ __ash_blog_newline_hack__ different from what you see in front of you. This is not a bad thing! If you had to use or write the actual underlying code, you’d get nothing done. Check out Nx’s documentation for more.__ash_blog_newline_hack__

__ash_blog_newline_hack__
defn softmax(t) do__ash_blog_newline_hack__  Nx.exp(t) / Nx.sum(Nx.exp(t))__ash_blog_newline_hack__end

__ash_blog_newline_hack__ Here is an example from__ash_blog_newline_hack__Nebulex , an ETS based caching library. In this example, the metaprogramming is quite hidden from you. The actual logic happens in a__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ compiler callback__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ . It involves rewriting the functions to intercept calls and decide if the cached version should be used, or if the value should be computed and cached. This kind of macro is quite magical given how indirect it is, but at the same time in applications where cached functions are common, it stops feeling like magic and starts feeling like a language feature. If I was looking at a large code-base that used this once, I’d likely be concerned. The mental overhead of understanding it for one single case likely isn’t worth it.__ash_blog_newline_hack__

__ash_blog_newline_hack__
defmodule MyApp.Example do__ash_blog_newline_hack__  use Nebulex.Caching__ash_blog_newline_hack____ash_blog_newline_hack__  @decorate cacheable(cache: MyApp.Cache, key: :cache_key)__ash_blog_newline_hack__  def get_by_name(name, age) do__ash_blog_newline_hack__    ...__ash_blog_newline_hack__  end__ash_blog_newline_hack__end

__ash_blog_newline_hack__ This one is from__ash_blog_newline_hack__Ash. We do similar magic to the__ash_blog_newline_hack__Nx example from above for this. Specifically, we have an expression that can be run natively to a data layer (i.e__ash_blog_newline_hack__SQL ) or in elixir. And Ash reserves the right to decide where it gets run, depending on the context and requirements.__ash_blog_newline_hack__

__ash_blog_newline_hack__
calculate :full_name, :string, expr(first_name <> " " <> last_name)

__ash_blog_newline_hack__ As you can see, all of these examples represent a significant amount of complexity boiled down to very simple macros. This will also be true of the upcoming examples of “configuration macros”, but the big difference here is that each macro call represents very complex code being executed on your behalf.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ Configuration Macros__ash_blog_newline_hack__ __ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ Lets take a look at some examples of macros that I would consider__ash_blog_newline_hack__ __ash_blog_newline_hack__ less__ash_blog_newline_hack__ __ash_blog_newline_hack__ magical.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ This one is likely familiar to most people who have worked with Elixir professionally. Here we have an Ecto.Schema. In this example we actually have three separate macros. The__ash_blog_newline_hack__schema/2 macro sets up some module attributes, and__ash_blog_newline_hack__field/2 adds to those module attributes.__ash_blog_newline_hack__timestamps/0 sets up the schema to track__ash_blog_newline_hack__inserted_at and__ash_blog_newline_hack__updated_at timestamps. On its own, however, an__ash_blog_newline_hack__Ecto.Schema doesn’t really__ash_blog_newline_hack__ __ash_blog_newline_hack__ do__ash_blog_newline_hack__ __ash_blog_newline_hack__ anything. There also isn’t a bunch of code hidden behind the scenes that you need to be aware of being executed. This sets up some information on the module that can be read back later. For example:__ash_blog_newline_hack__MyApp.MySchema.__schema__(:fields) would return__ash_blog_newline_hack__[:field_name, :inserted_at, :updated_at] in this case. This is a classic example of what I’d call a “configuration macro”.__ash_blog_newline_hack__

__ash_blog_newline_hack__
defmodule MyApp.MySchema do__ash_blog_newline_hack__  schema "table" do__ash_blog_newline_hack__    field :field_name, :type__ash_blog_newline_hack____ash_blog_newline_hack__    timestamps()__ash_blog_newline_hack__  end__ash_blog_newline_hack__end

__ash_blog_newline_hack__ Here we can see an example from__ash_blog_newline_hack__Absinthe ‘s documentation. Absinthe is a tool for building GraphQL apis with Elixir. This one slightly straddles the line between “configuration macros” and “metaprogramming macros”, but I think still lands on the side of a “configuration macro”. We configure a field in our GraphQL API, and how to resolve it. There are quite a few macros involved in building an API with__ash_blog_newline_hack__Absinthe . Here we configure what code gets run to resolve a given field, but ultimately the responsibility for what code runs is our own.__ash_blog_newline_hack__

__ash_blog_newline_hack__
defmodule BlogWeb.Schema do__ash_blog_newline_hack__  use Absinthe.Schema__ash_blog_newline_hack__  ...__ash_blog_newline_hack____ash_blog_newline_hack__  query do__ash_blog_newline_hack__    @desc "Get all posts"__ash_blog_newline_hack__    field :posts, list_of(:post) do__ash_blog_newline_hack__      resolve &Resolvers.Content.list_posts/3__ash_blog_newline_hack__    end__ash_blog_newline_hack__  end__ash_blog_newline_hack__end

__ash_blog_newline_hack__ Finally, lets take a look at some code from an__ash_blog_newline_hack__Ash.Resource . I’ll choose a relatively complex example, specifically the defining of an “action”.__ash_blog_newline_hack__

__ash_blog_newline_hack__
create :create do__ash_blog_newline_hack__  primary? true__ash_blog_newline_hack____ash_blog_newline_hack__  accept [:text]__ash_blog_newline_hack____ash_blog_newline_hack__  argument :public, :boolean do__ash_blog_newline_hack__    allow_nil? false__ash_blog_newline_hack__    default true__ash_blog_newline_hack__  end__ash_blog_newline_hack____ash_blog_newline_hack__  change fn changeset, _ ->__ash_blog_newline_hack__    if Ash.Changeset.get_argument(changeset, :public) do__ash_blog_newline_hack__      Ash.Changeset.force_change_attribute(changeset, :visibility, :public)__ash_blog_newline_hack__    else__ash_blog_newline_hack__      changeset__ash_blog_newline_hack__    end__ash_blog_newline_hack__  end__ash_blog_newline_hack____ash_blog_newline_hack__  change relate_actor(:author)__ash_blog_newline_hack__end

__ash_blog_newline_hack__ For those not familiar with Ash, lets break it down.__ash_blog_newline_hack__

__ash_blog_newline_hack____ash_blog_newline_hack__

__ash_blog_newline_hack__ These are all macros! However, they again map to an introspectable structure, acting more as configuration than as a traditional macro.__ash_blog_newline_hack__

__ash_blog_newline_hack__
iex(1)> Ash.Resource.Info.action(Twitter.Tweets.Tweet, :create)__ash_blog_newline_hack__%Ash.Resource.Actions.Create{__ash_blog_newline_hack__  name: :create,__ash_blog_newline_hack__  primary?: true,__ash_blog_newline_hack__  type: :create,__ash_blog_newline_hack__  __ash_blog_newline_hack__  arguments: [__ash_blog_newline_hack__    %Ash.Resource.Actions.Argument{__ash_blog_newline_hack__      name: :public,__ash_blog_newline_hack__      __ash_blog_newline_hack__    }__ash_blog_newline_hack__  ],__ash_blog_newline_hack__  changes: [__ash_blog_newline_hack__    %Ash.Resource.Change{...},__ash_blog_newline_hack__    %Ash.Resource.Change{__ash_blog_newline_hack__      change: {Ash.Resource.Change.RelateActor,__ash_blog_newline_hack__       [allow_nil?: false, relationship: :author]},__ash_blog_newline_hack__      __ash_blog_newline_hack__    }__ash_blog_newline_hack__  ],__ash_blog_newline_hack__  __ash_blog_newline_hack__}

__ash_blog_newline_hack__ This inversion of control is something we’re familiar with in other tools like__ash_blog_newline_hack__Plug or__ash_blog_newline_hack__Phoenix.Endpoint/Router . They are great examples of how this pattern can allow for code reuse, and make complex chains of behavior easier to reason about.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ Its not necessarily about macros__ash_blog_newline_hack__ __ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ Above I’ve made a case for macros not necessarily being what gives Ash its relatively steep learning curve. We’ve also create a large suite of tools to mitigate the difficulties that these configuration macros can have. Take a look at__ash_blog_newline_hack__ __ash_blog_newline_hack__ https://hex.pm/packages/spark__ash_blog_newline_hack__ __ash_blog_newline_hack__ for more information on those tools.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ With that said, in that one example above, we had to add five new words to our vocabulary. This is what can make Ash difficult. When you set out to write your own application patterns, you can let your own vocabulary evolve. This provides a natural learning curve. I’d also argue that it gives you tons of opportunities to repeat the mistakes of every developer who went through that same process.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ Therein lies the rub__ash_blog_newline_hack__ __ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ Ash presents a huge suite of integrated tools, a suite that gets bigger and bigger every day and, in order to leverage it, you need to learn a whole new way of doing things. This is a very valid reason not to use Ash. Our biggest proponents will tell you that it’s worth it to put in the effort to learn these tools. As would I. And I don’t mean for the idea of what the framework may become, but for the benefits you can get right now.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ With that said, I think a diversity of mindsets is the most important thing that we can foster as a community, and there is a fine line between telling people “Hey, I think we’ve got some good ideas over here, I think you should check them out”, and saying “Hey, this is the right way to do things, stop doing things your way and do them our way”. So while I’m of course a proponent of Ash, my plan is not to__ash_blog_newline_hack__ __ash_blog_newline_hack__ talk anyone into using it__ash_blog_newline_hack__ __ash_blog_newline_hack__ . I have one simple goal, which is to continue to expand this integrated suite of tools, adding new capabilities that are only possible for things built in this way. Ash is a snowball, rolling down a hill, and it has barely even begun to gather snow. By doing this, we move the threshold by which cost of learning the “Ash way” is offset by the benefits. For many users (more every day) that threshold has already been crossed. For others, it may never be crossed, and thats okay 😊. If the tools we build can help even one person find success on their Elixir journey, then I’m a happy camper.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ Parting Words__ash_blog_newline_hack__ __ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ The cost of learning something like Ash is not trivial. It depends on you, your problem space, your experience level, and many other factors as to whether or not that juice is worth the squeeze. My goal, and the core team’s goal, is to continue to provide leverage for those already using Ash. To take the patterns we’ve set down and take them even further, increasing the value of our existing users’ investments. Partially because we count among those users, but also because we believe in what we’re building and its ability to help us and others build bigger and better software.__ash_blog_newline_hack__

' +body_html: '

__ash_blog_newline_hack__ A common criticism of Ash is that it has too many macros. This is an understandable position to take, but I think its important to distinguish between two main kinds of macros. One of them should be used extremely sparingly, and the other I think its okay to use a bit more liberally. We’ll call them “configuration macros” and “metaprogramming macros”. Keep in mind these names are a bit made up and the boundary between the two is not always cut and dry. When people talk about “magic” in the context of macros, they are mostly talking about “metaprogramming macros”. Lets take a look at some examples.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ Metaprogramming Macros__ash_blog_newline_hack__ __ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ This snippet is from__ash_blog_newline_hack__ __ash_blog_newline_hack__ NX__ash_blog_newline_hack__ __ash_blog_newline_hack__ . This function can run as regular elixir code, or it can be compiled to run using multiple backends meant for numerical processing, including running on the GPU. The code that is actually compiled under the hood looks__ash_blog_newline_hack__ __ash_blog_newline_hack__ dramatically__ash_blog_newline_hack__ __ash_blog_newline_hack__ different from what you see in front of you. This is not a bad thing! If you had to use or write the actual underlying code, you’d get nothing done. Check out Nx’s documentation for more.__ash_blog_newline_hack__

__ash_blog_newline_hack__
defn softmax(t) do__ash_blog_newline_hack__  Nx.exp(t) / Nx.sum(Nx.exp(t))__ash_blog_newline_hack__end

__ash_blog_newline_hack__ Here is an example from__ash_blog_newline_hack__Nebulex , an ETS based caching library. In this example, the metaprogramming is quite hidden from you. The actual logic happens in a__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ compiler callback__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ . It involves rewriting the functions to intercept calls and decide if the cached version should be used, or if the value should be computed and cached. This kind of macro is quite magical given how indirect it is, but at the same time in applications where cached functions are common, it stops feeling like magic and starts feeling like a language feature. If I was looking at a large code-base that used this once, I’d likely be concerned. The mental overhead of understanding it for one single case likely isn’t worth it.__ash_blog_newline_hack__

__ash_blog_newline_hack__
defmodule MyApp.Example do__ash_blog_newline_hack__  use Nebulex.Caching__ash_blog_newline_hack____ash_blog_newline_hack__  @decorate cacheable(cache: MyApp.Cache, key: :cache_key)__ash_blog_newline_hack__  def get_by_name(name, age) do__ash_blog_newline_hack__    ...__ash_blog_newline_hack__  end__ash_blog_newline_hack__end

__ash_blog_newline_hack__ This one is from__ash_blog_newline_hack__Ash. We do similar magic to the__ash_blog_newline_hack__Nx example from above for this. Specifically, we have an expression that can be run natively to a data layer (i.e__ash_blog_newline_hack__SQL ) or in elixir. And Ash reserves the right to decide where it gets run, depending on the context and requirements.__ash_blog_newline_hack__

__ash_blog_newline_hack__
calculate :full_name, :string, expr(first_name <> " " <> last_name)

__ash_blog_newline_hack__ As you can see, all of these examples represent a significant amount of complexity boiled down to very simple macros. This will also be true of the upcoming examples of “configuration macros”, but the big difference here is that each macro call represents very complex code being executed on your behalf.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ Configuration Macros__ash_blog_newline_hack__ __ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ Lets take a look at some examples of macros that I would consider__ash_blog_newline_hack__ __ash_blog_newline_hack__ less__ash_blog_newline_hack__ __ash_blog_newline_hack__ magical.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ This one is likely familiar to most people who have worked with Elixir professionally. Here we have an Ecto.Schema. In this example we actually have three separate macros. The__ash_blog_newline_hack__schema/2 macro sets up some module attributes, and__ash_blog_newline_hack__field/2 adds to those module attributes.__ash_blog_newline_hack__timestamps/0 sets up the schema to track__ash_blog_newline_hack__inserted_at and__ash_blog_newline_hack__updated_at timestamps. On its own, however, an__ash_blog_newline_hack__Ecto.Schema doesn’t really__ash_blog_newline_hack__ __ash_blog_newline_hack__ do__ash_blog_newline_hack__ __ash_blog_newline_hack__ anything. There also isn’t a bunch of code hidden behind the scenes that you need to be aware of being executed. This sets up some information on the module that can be read back later. For example:__ash_blog_newline_hack__MyApp.MySchema.__schema__(:fields) would return__ash_blog_newline_hack__[:field_name, :inserted_at, :updated_at] in this case. This is a classic example of what I’d call a “configuration macro”.__ash_blog_newline_hack__

__ash_blog_newline_hack__
defmodule MyApp.MySchema do__ash_blog_newline_hack__  schema "table" do__ash_blog_newline_hack__    field :field_name, :type__ash_blog_newline_hack____ash_blog_newline_hack__    timestamps()__ash_blog_newline_hack__  end__ash_blog_newline_hack__end

__ash_blog_newline_hack__ Here we can see an example from__ash_blog_newline_hack__Absinthe ‘s documentation. Absinthe is a tool for building GraphQL apis with Elixir. This one slightly straddles the line between “configuration macros” and “metaprogramming macros”, but I think still lands on the side of a “configuration macro”. We configure a field in our GraphQL API, and how to resolve it. There are quite a few macros involved in building an API with__ash_blog_newline_hack__Absinthe . Here we configure what code gets run to resolve a given field, but ultimately the responsibility for what code runs is our own.__ash_blog_newline_hack__

__ash_blog_newline_hack__
defmodule BlogWeb.Schema do__ash_blog_newline_hack__  use Absinthe.Schema__ash_blog_newline_hack__  ...__ash_blog_newline_hack____ash_blog_newline_hack__  query do__ash_blog_newline_hack__    @desc "Get all posts"__ash_blog_newline_hack__    field :posts, list_of(:post) do__ash_blog_newline_hack__      resolve &Resolvers.Content.list_posts/3__ash_blog_newline_hack__    end__ash_blog_newline_hack__  end__ash_blog_newline_hack__end

__ash_blog_newline_hack__ Finally, lets take a look at some code from an__ash_blog_newline_hack__Ash.Resource . I’ll choose a relatively complex example, specifically the defining of an “action”.__ash_blog_newline_hack__

__ash_blog_newline_hack__
create :create do__ash_blog_newline_hack__  primary? true__ash_blog_newline_hack____ash_blog_newline_hack__  accept [:text]__ash_blog_newline_hack____ash_blog_newline_hack__  argument :public, :boolean do__ash_blog_newline_hack__    allow_nil? false__ash_blog_newline_hack__    default true__ash_blog_newline_hack__  end__ash_blog_newline_hack____ash_blog_newline_hack__  change fn changeset, _ ->__ash_blog_newline_hack__    if Ash.Changeset.get_argument(changeset, :public) do__ash_blog_newline_hack__      Ash.Changeset.force_change_attribute(changeset, :visibility, :public)__ash_blog_newline_hack__    else__ash_blog_newline_hack__      changeset__ash_blog_newline_hack__    end__ash_blog_newline_hack__  end__ash_blog_newline_hack____ash_blog_newline_hack__  change relate_actor(:author)__ash_blog_newline_hack__end

__ash_blog_newline_hack__ For those not familiar with Ash, lets break it down.__ash_blog_newline_hack__

__ash_blog_newline_hack____ash_blog_newline_hack__

__ash_blog_newline_hack__ These are all macros! However, they again map to an introspectable structure, acting more as configuration than as a traditional macro.__ash_blog_newline_hack__

__ash_blog_newline_hack__
iex(1)> Ash.Resource.Info.action(Twitter.Tweets.Tweet, :create)__ash_blog_newline_hack__%Ash.Resource.Actions.Create{__ash_blog_newline_hack__  name: :create,__ash_blog_newline_hack__  primary?: true,__ash_blog_newline_hack__  type: :create,__ash_blog_newline_hack__  __ash_blog_newline_hack__  arguments: [__ash_blog_newline_hack__    %Ash.Resource.Actions.Argument{__ash_blog_newline_hack__      name: :public,__ash_blog_newline_hack__      __ash_blog_newline_hack__    }__ash_blog_newline_hack__  ],__ash_blog_newline_hack__  changes: [__ash_blog_newline_hack__    %Ash.Resource.Change{...},__ash_blog_newline_hack__    %Ash.Resource.Change{__ash_blog_newline_hack__      change: {Ash.Resource.Change.RelateActor,__ash_blog_newline_hack__       [allow_nil?: false, relationship: :author]},__ash_blog_newline_hack__      __ash_blog_newline_hack__    }__ash_blog_newline_hack__  ],__ash_blog_newline_hack__  __ash_blog_newline_hack__}

__ash_blog_newline_hack__ This inversion of control is something we’re familiar with in other tools like__ash_blog_newline_hack__Plug or__ash_blog_newline_hack__Phoenix.Endpoint/Router . They are great examples of how this pattern can allow for code reuse, and make complex chains of behavior easier to reason about.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ Its not necessarily about macros__ash_blog_newline_hack__ __ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ Above I’ve made a case for macros not necessarily being what gives Ash its relatively steep learning curve. We’ve also create a large suite of tools to mitigate the difficulties that these configuration macros can have. Take a look at__ash_blog_newline_hack__ __ash_blog_newline_hack__ spark__ash_blog_newline_hack__ __ash_blog_newline_hack__ for more information on those tools.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ With that said, in that one example above, we had to add five new words to our vocabulary. This is what can make Ash difficult. When you set out to write your own application patterns, you can let your own vocabulary evolve. This provides a natural learning curve. I’d also argue that it gives you tons of opportunities to repeat the mistakes of every developer who went through that same process.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ Therein lies the rub__ash_blog_newline_hack__ __ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ Ash presents a huge suite of integrated tools, a suite that gets bigger and bigger every day and, in order to leverage it, you need to learn a whole new way of doing things. This is a very valid reason not to use Ash. Our biggest proponents will tell you that it’s worth it to put in the effort to learn these tools. As would I. And I don’t mean for the idea of what the framework may become, but for the benefits you can get right now.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ With that said, I think a diversity of mindsets is the most important thing that we can foster as a community, and there is a fine line between telling people “Hey, I think we’ve got some good ideas over here, I think you should check them out”, and saying “Hey, this is the right way to do things, stop doing things your way and do them our way”. So while I’m of course a proponent of Ash, my plan is not to__ash_blog_newline_hack__ __ash_blog_newline_hack__ talk anyone into using it__ash_blog_newline_hack__ __ash_blog_newline_hack__ . I have one simple goal, which is to continue to expand this integrated suite of tools, adding new capabilities that are only possible for things built in this way. Ash is a snowball, rolling down a hill, and it has barely even begun to gather snow. By doing this, we move the threshold by which cost of learning the “Ash way” is offset by the benefits. For many users (more every day) that threshold has already been crossed. For others, it may never be crossed, and thats okay 😊. If the tools we build can help even one person find success on their Elixir journey, then I’m a happy camper.__ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ __ash_blog_newline_hack__ Parting Words__ash_blog_newline_hack__ __ash_blog_newline_hack__

__ash_blog_newline_hack__

__ash_blog_newline_hack__ The cost of learning something like Ash is not trivial. It depends on you, your problem space, your experience level, and many other factors as to whether or not that juice is worth the squeeze. My goal, and the core team’s goal, is to continue to provide leverage for those already using Ash. To take the patterns we’ve set down and take them even further, increasing the value of our existing users’ investments. Partially because we count among those users, but also because we believe in what we’re building and its ability to help us and others build bigger and better software.__ash_blog_newline_hack__

' inserted_at: "2023-05-11 19:39:18.027487Z" updated_at: "2023-05-11 20:00:29.800042Z" --- @@ -149,7 +149,7 @@ This inversion of control is something we're familiar with in other tools like ` ## Its not necessarily about macros -Above I've made a case for macros not necessarily being what gives Ash its relatively steep learning curve. We've also create a large suite of tools to mitigate the difficulties that these configuration macros can have. Take a look at [https://hex.pm/packages/spark](spark) for more information on those tools. +Above I've made a case for macros not necessarily being what gives Ash its relatively steep learning curve. We've also create a large suite of tools to mitigate the difficulties that these configuration macros can have. Take a look at [spark](https://hex.pm/packages/spark) for more information on those tools. With that said, in that one example above, we had to add five new words to our vocabulary. This is what can make Ash difficult. When you set out to write your own application patterns, you can let your own vocabulary evolve. This provides a natural learning curve. I'd also argue that it gives you tons of opportunities to repeat the mistakes of every developer who went through that same process.