whipped this up in a couple of hours in a frenzy. still not entirely sure why, just felt like it. i don't really have a use for it at the moment.
it is, however, pretty elegant to do composition in IMO. some validators are built using other validators as a dogfooding thing, eg. the maybe(v) validator is an ordered-choice between nil and v. maybe i'll use it in porom idk
a monad is a monoid in the category of endofunctors
but basically, maybe(v) is a monad, or well, not really. It needs to implement a "bind" function (for example, and_then is a "bind" function) to chain monads.
A monad M(T) is a wrapper around any type T, that implements a f(x) -> M(x) (a function that wraps a value in the monad), and a f(in: M(A), closure: g(A)) -> M(B)... which is the "bind" function, it takes a monad, runs a function on the underlying value and produces a new monad.
Suggestion from someone who's worked a lot with user data; have a parsing API. instead of validating, what you often want is parsing.
For example, an "age" input field will return a string (in a webpage, from the command line, piped from another output, ...), but you want a number. User input is almost always strings; often interexchange formats like JSON are also strings or very simple datatypes (e.g, no dates, no ints, etc).
instead of this:
local ok, err = over_13(13)
print(ok) -- prints the original number if > 13
you'd have that:
local ok, err = over_13.parse("13")
print(ok.data) -- prints the parsed number if > 13
end
If parse() is your central API, then you don't need a different method, it's just an example.
This comes particularly handy for forms with multiple fields, and allows you to return well typed full objects, with possibly transformed fields, why not. For example, a field with username, password and conform password could skip the confirmation field in the parsed result.
As for monads: at its most basic it's an interface. If an object follows this interface, it's a monad. Why do we speak specifically about monads then? Because it's a very useful interface. You could compare it to the Iterator interface; it's ubiquitous enough that you find it in many languages; it's generic and useful enough that it has many applications. As a result, we can speak of "iterator" in any language and people will mostly know what we mean. An iterator will allow you to operate over a list of values, either bound, or unbound, either sync, or async.
A Monad then, is an interface that predicates the following functionality: it can take a value, return a value, and take a function to transform the value. That's all I think.
yeah, that's of course a better idea. this is more of an exercise, i just wanted really solid combinatorics. i might make a "sequel" to this that actually does parse, but i really like the elegance so i'll see if i can keep that if i do work on it.