Why don’t we have {Module, args, opts} as a way to start a child under supervisor

In Supervisor.start_link, we have three ways to start children

  • a module
  • a {module, arg} tuple
  • a child specification as a map with at least the :id and :start fields
  • or a tuple with 6 elements generated by Supervisor.Spec (deprecated)

For naming the child, the {module, arg} tuple works fine if the module start_link is start_link(opts \\ []), but for start_link(args, opts), how do we pass in both args and opts? Why don’t we have {module, args, opts} so we can pass in [name: RegisterThisName] so we can refer it with the name later?

How do I give it a name in this case?

children = [
 {Module1, name: Module1}
 {Module2, name: Module2}
 {Module3, %Struct{...}, name: Module3} <—— how to name it?
]

Contrary to the old ˋSupervisor.Specˋ way the new tuple form is not meant to define multiple parameters for start_link. ˋ{mod, arg}ˋ will result in a call to ˋmod.child_spec/1ˋ. That function can only ever be arity-1, so different tuples don‘t make sense.

Within ˋchild_spec/1ˋ you could divide the arguments up again to make the supervisor child by calling ˋmod.start_link/2ˋ, but that will require a custom ˋchild_spec/1ˋ. The default generated by ˋuse GenServerˋ does make the arguments sent to ˋchild_spec/1ˋ be passed to ˋmod.start_link/1ˋ.

I‘ve recently written a blogpost on the topic, if you need more details:

1 Like

Sorry for the late reply. Good post!

I like the example of {Registry, keys: :unique, name: Registry.ViaTest}. Is it not common to pass a struct into start_link to initialize a GenServer, but instead passing in keyword?

I’ve mostly seen keyword lists for opts of various means. Structs are most often only used if they hold somehow preaggregated/prevalidated config.

2 Likes