`allow_nil?` and it's default
- Dominant language
- Elixir
- Stars
- 2.5k
- Forks
- 422
- Avg merge
- 23h 26m
- Merged PRs (30d)
- 46
Description
A personal opinion that I wanted to bring up now that Ash 4 is somewhere still distant but clear horizon.
## The issue
Nullability of everything by default with `allow_nil?: true` is a bad default.
### Prevalence
Looking at distribution of nullable vs non-nullable fields in code bases - nullability of attributes/arguments is in a minority.
Looking at open source reference projects of RealWorld and Tololo:
**RealWolrd**
13 attributes - only 2 do not have `allow_nil?: false`.
13 arguments - only 3 do not have `allow_nil?: false`.
**Tololo**
80 attributes - 31 without `allow_nil?: false` or `default` (seems like exclusive options there).
25 arguments - only 5 are nullable (and in 2 of those cases I think it is actually a mistake and based on code they should not be).
### Consistency
Elixir does not have convention of `number` and `number!` with implicit nullability - it has `number` and `number | nil`.
Other `allow_something?` options (`allow_unregistered?`, `allow_empty?`, `allow_forbidden_field?`, `allow_expr?`) all have false as default.
Because of that the default is very much unintuitive. In my personal experience with helping people to learn Ash this option's default always conflicts with people's expectations.
I would assume that the propagation of default nullability went like this sql -> ecto -> ash, but worth noting that Elixir's nil and sql null have (or at least had for long time) a different meaning and behavior (for equality) - an absence of value vs unknown.
### Naming
If the option was named `required?` it would be a little bit more understandable, but as of right now it is as if it is called `optional?` with the default of true which is strange.
Noting that `required?` would not work in context of calculations, aggregates. But so is `allow_nil?` because for those things you don't actually allow something you annotate what they return (correct me if I am wrong).
### Readability
Combination of all previous points makes code less readable even if you know the default. It just creates noise.
### Strictness
It would have helped if the divergence from expectations and conventions was with the purpose of making code more strict and security - like addition of `public?` or change of `accept` in Ash 3. But unfortunately it is other way around - unexpected null that were allowed by default can cause an error or two.
## A solution
Flipping default value to false in Ash 4 might be enough. But just as an alternative solution to think about:
What if we used tuple form of type - like `{:array, ...}` or `{:map, ...}` - for nil?
```elixir
argument :important, :number
argument :whatever, {:null, :number}
```
## Note
Not really attached to this solution (so it is "a solution" not "the solution", just wanted to share the idea) but pretty sure about the presence of the issue.
Contributor guide
Research direction
Start by reviewing the current allow_nil? defaults and how nullability is represented for attributes, arguments, calculations, and aggregates, then read the discussion for a decision. Done means an agreed Ash 4 design is documented and its affected behavior is covered consistently.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- elixir
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100