Recursive Union Error on 3.1 Upgrade

Hi, all

Here’s an extract of a union in my codebase:

defmodule Condition do
  use Ash.Type.NewType, subtype_of: :union, constraints: [types: [
    {Always, [type: Always]},
    {ResponseEquals, [type: ResponseEquals, tag: :type, tag_value: "response_equals"]},
    {AnyTrue, [type: AnyTrue, tag: :type, tag_value: "any_true"]},
    {AllTrue, [type: AllTrue, tag: :type, tag_value: "all_true"]},
  ]]
end

Upgrading to 3.1 causes this union to throw a compile error:

** (UndefinedFunctionError) function AnyTrue.init/1 is undefined (function not available)
    AnyTrue.init([])
    (ash 3.1.0) lib/ash/type/union.ex:72: anonymous fn/2 in Ash.Type.Union.init/1
    (elixir 1.17.2) lib/enum.ex:4858: Enumerable.List.reduce/3
    (elixir 1.17.2) lib/enum.ex:2585: Enum.reduce_while/3
    (ash 3.1.0) lib/ash/type/union.ex:68: Ash.Type.Union.init/1
    (ash 3.1.0) lib/ash/type/type.ex:627: Ash.Type.init/2
    (ash 3.1.0) lib/ash/type/type.ex:1735: Ash.Type.set_type_transformation/1
    (spark 2.2.8) lib/spark/dsl/entity.ex:265: Spark.Dsl.Entity.build/3

Only AnyTrue and AllTrue are affected and both of them are recursive on Condition as such:

defmodule AnyTrue do
  use Ash.Resource, data_layer: :embedded

  attributes do
    attribute :conditions, {:array, Condition}, allow_nil?: false, public?: true, constraints: [min_length: 1]
  end
end

Is this a bug or a feature in 3.1? Is there a way around it?

Ah, so before, unions didn’t call Ash.Type.init on their variants, but that can cause problems in certain scenarios.

I’m not sure if this will be problematic for you or not, but in main of ash there is now an option that can be placed on a union type member called init?: false to tell it not to initialize a given variant.

defmodule Condition do
  use Ash.Type.NewType, subtype_of: :union, constraints: [types: [
    {Always, [type: Always, init?: false]},
    {ResponseEquals, [type: ResponseEquals, tag: :type, tag_value: "response_equals", init?: false]},
    {AnyTrue, [type: AnyTrue, tag: :type, tag_value: "any_true", init?: false]},
    {AllTrue, [type: AllTrue, tag: :type, tag_value: "all_true", init?: false]},
  ]]
end

Please try out main and let me know if that resolves your issue.

Thankfully, it’s not an issue this time around :slight_smile:

main resolves it. Thank you.