I am updating my input validation library to better handle cases where you want to run validation “onBlur” and not “onInput”, in response to this issue: https://github.com/ericgj/elm-validation/issues/3
Essentially, it means adding a state called
Unvalidated String – when input is happening but you haven’t yet run validation on it.
The question arose whether then we need an
Initial (empty) state, since
Initial could be modelled as
My view is it may be useful to distinguish cases where a user starts entering and then deletes their input, versus a truly unedited initial state. (Also, given the structure is monadic it seems odd not to have a true empty state, although I guess I could rewrite
initial = Unvalidated "".)
But, it was objected that this would force users to handle two states that in most cases are for practical purposes the same. The data structure is not opaque so potentially people would be caseing on it. (Although I think I give enough basic functions you really shouldn’t need to, IMO.)
Anyway, long story short, if anyone else uses this library now or would like to use it, let me know what you think, either here or on the github issue.
I’ve added a branch which has distinct
Unvalidated String states, and an example if you are curious what it would look like.
We recently were having a similar discussion on Slack, and I mentioned that I considered using your library for my project but because there was no way to handle state of the input without validating it, I created my own type:
type Validation value
= Pristine String
| Dirty String
| Invalid String String
| Valid value
I used this type to model the different state of my input field.
Pristine can hold an initial value. As soon as the user touches the input, I changed it to
Dirty with the new value. This is excellent when you have a required field that the user just jumped over. An empty
String by itself can’t differentiate that it is empty because that’s the initial value or not.
Since I don’t want to pester my user with errors as they’re typing, I delay the validation until a second after they finish writing. At that moment, I pass it through my validator and it returns either
Invalid depending on the result.
At least for me, it makes sense to distinguish between the state before and after validation, and this type works for me.
I like how you modelled this, but to be splitting hairs,
Pristine should hold a
value rather than a
String. I also see how in your case (validation with a timer) you need to distinguish between
Pristine "" and
The reason why I would avoid placing a
value instead of a
String in the
Pristine constructor is because there are some
values that can’t be created directly.
Let’s say you want to ask the user to input a prime number, and to model that you decide to use the
Prime type in
Fresheyeball/elm-restrict-number. You can’t create this value directly, since the only constructor offered returns a
Maybe Prime. If you had another
Prime to provide as a default, there would be no problem, but in this example there isn’t.
On the other hand, if you want to provide 3 as a default prime number, you set
Pristine "3", and when it is time to validate it, if the user did not change the value, it will turn to
Valid (Prime 3), otherwise if the user provided a
"4", it will turn into an
Invalid "Please provide a prime number" "4".
So, actually I meant that
Pristine should wrap a
Maybe value - because
of course your form does not need to provide an initial value. The idea
is that if validation and conversion produce a
value, then if you provide
an initial value it should be valid
value. Otherwise, your type allows
the programmer to create a
Validation Prime of, say,
This is why
Pristine Maybe value would create a more consistent interface
for the programmer to code to.
Having said that, I still think that for most application, distinguishing
Dirty is a bit overkill.