I like this feature a lot, but dislike the term enforce. IMO, it muddies things and is much less clear than require. In my experience, the term required is generally used to indicate fields that must be present. For example, JSON schema uses that term. enforce gives me the sense that some kind of validation policy is applied but does not describe what the validation policy is. Does it enforce the type of values allowed for the named fields? Does it enforce the fields are present? Does it enforce the named fields are *not* present? Does enforce: [:percent] validate that percent is between 1 and 100?
Obviously, given there is no further information provided with the struct definition, it would be reasonable to assume that it means that the presence of the named fields is enforced, but I fail to see how enforce is preferable to require. Regardless of which word is chosen, I think it would be easy to write the past-tense form of the word (e.g. enforced or required instead of enforce or require). With the keyword argument approach, defstruct could provide an nice error when you use the wrong form. With the module attribute approach, it wouldn’t really make sense to detect that, since module attributes can be user-defined and any attribute is valid. So I would vote in favor of: defstruct [:name, :age], require: [:name] …and also ask that it provides a nice error if you use required: or any other invalid option. Myron On Wednesday, May 25, 2016 at 8:16:20 AM UTC-7, Chris McCord wrote: > > I'm onboard with the module attribute approach as it aligns with what we > already have. The potential existing `@enforce` conflicts would be > extremely minimal so +1 to eric's proposal. > > On May 25, 2016, at 11:11 AM, José Valim <[email protected] > <javascript:>> wrote: > > Thanks Mike and Parker for the feedback so far. > > Eric has proposed another approach, via module attributes: > > @enforce [:name] > > defstruct [:name, :age] > > > One of the benefits of using module attributes is that it matches nicely > the already supported @derive attribute: > > @derive Poison.Encoder > > @enforce [:name] > > defstruct [:name, :age] > > > Furthermore, the module attribute composes better. For example, if we want > to support @enforce in defexception (which is based defstruct), we don't > need to change anything, it just works: > > @enforce [:message] > > defexception [:message] > > > The same could work with Ecto: > > @enforce [:name] > schema "users" do > ... > end > > > The only downside is that a module attribute can be silently defined. For > example, someone may set @enforce [:foo, :bar] in their module for other > reasons and now they will conflict. However, in such cases we can easily > check and guarantee all fields given to @enforce are also defined in the > struct. > > Can anyone think of other pros-and-cons here? > > > > *José Valim* > www.plataformatec.com.br > Skype: jv.ptec > Founder and Director of R&D > > On Wed, May 25, 2016 at 4:33 PM, Parker Selbert <[email protected] > <javascript:>> wrote: > >> The most recent proposal, using `enforce: [:name]` is extremely clear, >> and nicely backward compatible. Overall I love the recent focus on >> enforcing the presence of attributes and data integrity. >> >> — Parker >> >> >> On Wed, May 25, 2016, at 02:10 AM, Mike Evans wrote: >> >> +1 as it solves the immediate clarity issue nicely with an undeniably >> strong word. >> >> >> >> On May 25, 2016, at 2:07 AM, José Valim <[email protected] >> <javascript:>> wrote: >> >> Given Peter's feedback, what if we define it as: >> >> >> defstruct [:name, :age], enforce: [:name] >> >> >> The idea of picking :enforce instead of :required is to avoid any >> possible confusion that some of those fields won't be effectively present >> in the struct. >> >> Thoughts? >> >> >> >> >> *José Valim* >> >> www.plataformatec.com.br >> Skype: jv.ptec >> Founder and Director of R&D >> >> >> >> On Wed, May 25, 2016 at 3:07 AM, eksperimental <[email protected] >> <javascript:>> wrote: >> >> José: great new feature. Definitely a needed one! >> and +1 on Peter Hamilton's suggestion on how to define required fields. >> >> An extra feature that will save us developers a lot of headeaches is >> the ability to define a list of accepted values per field. As as the >> current state, functions silently fail unexpected values are given >> >> On Tue, 24 May 2016 23:35:34 +0000 >> Peter Hamilton <[email protected] <javascript:>> wrote: >> >> > I am not a fan of the proposed signature. Generally when we have >> > multiple clauses of different arity, we try to make them purely >> > extensions of the first. In the proposal, we go from (fields) to >> > (optional_fields, required_fields). While one could say that it's >> > really (optional_fields) to (optional_fields, required_fields), I >> > think that's changing the semantics of the current signature, which >> > to me is all the fields. I will submit, however, that this is a bit >> > subjective and realistically the same in practice. >> > >> > I would propose instead: >> > >> > defstruct [age: nil, name: nil], required: [:name] >> > >> > It not only maintains semantic backwards compatibility, but there >> > isn't an implicit meaning behind the two different arguments. It's >> > very clear that we have a list of arguments then an explicit list of >> > required fields. >> > >> > On Tue, May 24, 2016 at 4:26 PM José Valim >> > <[email protected] <javascript:>> wrote: >> > >> > > To clarify, both :age and :name fields will be present in the >> > > underlying User struct/map. The proposal is only about fields which >> > > must be enforced when building the structure. >> > > >> > > We will likely need better names than optional/required. >> > > >> > > *José Valim* >> > > www.plataformatec.com.br >> > > Skype: jv.ptec >> > > Founder and Director of R&D >> > > >> > > On Wed, May 25, 2016 at 1:17 AM, José Valim < >> > > [email protected] <javascript:>> wrote: >> > > >> > >> Hello everyone, >> > >> >> > >> I would like to propose an extension to defstruct that will require >> > >> certain fields to be given when expanding it. Here is an example: >> > >> >> > >> defmodule User do >> > >> >> > >> defstruct [age: nil], # optional >> > >> >> > >> [name: nil] # required >> > >> >> > >> end >> > >> >> > >> >> > >> With this feature, %User{} will fail as the :name field was not >> > >> specified. %User{name: "foo"} or %User{name: nil} will both work as >> > >> expected. The main use case is to make sure all important fields >> > >> are set when building the data. For example, we can use such >> > >> fields in the new date time types to enforce proper data >> > >> representation. >> > >> >> > >> *Extra notes* >> > >> >> > >> 1. The required fields are given as second argument to defstruct >> > >> as the API must remain backwards compatibility >> > >> >> > >> 2. The fields are required only when building structs. Matching >> > >> will always work without specifying any field, for example: >> > >> %User{} = user >> > >> >> > >> 3. The Kernel.struct/2 function, used to build structs >> > >> dynamically, won't check for required keys. Kernel.struct!/2 >> > >> should be used if you want to check for required keys (and also >> > >> check that no extra keys are given) >> > >> >> > >> 4. defexception will leverage the same functionality >> > >> >> > >> *Implementation* >> > >> >> > >> Implementation-wise, structs will now defined a __struct__/1 >> > >> function, besides the regular __struct__/0 function. It has not >> > >> been decided yet how such function will behave given it must work >> > >> both for compile-time (%User{}) and runtime (struct! User, %{}) >> > >> checks. >> > >> >> > >> *Feedback* >> > >> >> > >> Now it is your turn. :) >> > >> >> > >> *José Valim* >> > >> www.plataformatec.com.br >> > >> Skype: jv.ptec >> > >> Founder and Director of R&D >> > >> >> > > >> > > -- >> > > You received this message because you are subscribed to the Google >> > > Groups "elixir-lang-core" group. >> > > To unsubscribe from this group and stop receiving emails from it, >> > > send an email to [email protected] <javascript:>. >> > > To view this discussion on the web visit >> > > >> https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2B5b%2BxxcvMOL-n6XyJ4mcQcumTU5B0AhjaTuc7qk-0P1g%40mail.gmail.com >> > > < >> https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2B5b%2BxxcvMOL-n6XyJ4mcQcumTU5B0AhjaTuc7qk-0P1g%40mail.gmail.com?utm_medium=email&utm_source=footer >> > >> > > . >> > > For more options, visit https://groups.google.com/d/optout. >> > > >> > >> >> -- >> You received this message because you are subscribed to the Google Groups >> "elixir-lang-core" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected] <javascript:>. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/elixir-lang-core/20160525080713.279fe1dd.eksperimental%40autistici.org >> . >> >> For more options, visit https://groups.google.com/d/optout. >> >> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "elixir-lang-core" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected] <javascript:>. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4K5-pCjrDudw1MgXbGcwfhfn%3Da%2BpukmO00cgz1nsYg9Zg%40mail.gmail.com >> >> <https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4K5-pCjrDudw1MgXbGcwfhfn%3Da%2BpukmO00cgz1nsYg9Zg%40mail.gmail.com?utm_medium=email&utm_source=footer> >> . >> For more options, visit https://groups.google.com/d/optout. >> >> >> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "elixir-lang-core" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected] <javascript:>. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/elixir-lang-core/6C3AEDA3-34F1-4CED-8B4E-B1769C83D812%40silljays.com >> >> <https://groups.google.com/d/msgid/elixir-lang-core/6C3AEDA3-34F1-4CED-8B4E-B1769C83D812%40silljays.com?utm_medium=email&utm_source=footer> >> . >> For more options, visit https://groups.google.com/d/optout. >> >> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "elixir-lang-core" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected] <javascript:>. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/elixir-lang-core/1464186821.454905.618394761.3E78A7BC%40webmail.messagingengine.com >> >> <https://groups.google.com/d/msgid/elixir-lang-core/1464186821.454905.618394761.3E78A7BC%40webmail.messagingengine.com?utm_medium=email&utm_source=footer> >> . >> >> For more options, visit https://groups.google.com/d/optout. >> > > > -- > You received this message because you are subscribed to the Google Groups > "elixir-lang-core" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected] <javascript:>. > To view this discussion on the web visit > https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4Ji7JxRFN7fw72VNwqD%2BaqPZ3MQy7EJi-DcUztG-S1KKA%40mail.gmail.com > > <https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4Ji7JxRFN7fw72VNwqD%2BaqPZ3MQy7EJi-DcUztG-S1KKA%40mail.gmail.com?utm_medium=email&utm_source=footer> > . > For more options, visit https://groups.google.com/d/optout. > > > -- You received this message because you are subscribed to the Google Groups "elixir-lang-core" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/c73d1719-c0d1-4066-b5f7-7359db1ba64e%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.
