I have a good grasp on imperative programming, but now I learn myself a Haskell for great good.
I think, I have a good theoretical understanding of Monads, Functors and Applicatives, but I need some practice. And for practice I sometimes bring some bits from my current work tasks.
And I'm stuck a bit with combining stuff in applicative way
First question
I have two functions for validation:
import Prelude hiding (even)
even :: Integer -> Maybe Integer
even x = if rem x 2 == 0 then Just x else Nothing
isSmall :: Integer -> Maybe Integer
isSmall x = if x < 10 then Just x else Nothing
Now I want validate :: Integer -> Maybe Integer built from even and isSmall
My best solution is
validate a = isSmall a *> even a *> Just a
And it's not point free
I can use a monad
validate x = do
even x
isSmall x
return x
But why use Monad, if (I suppose) all I need is an Applicative? (And it still not point free)
Is it a better (and more buitiful way) to do that?
Second question
Now I have two validators with different signatures:
even = ...
greater :: (Integer, Integer) -> Maybe (Integer, Integer)
-- tuple's second element should be greater than the first
greater (a, b) = if a >= b then Nothing else Just (a, b)
I need validate :: (Integer, Integer) -> Maybe (Integer, Integer), which tries greater on the input tuple and then even on the tuple's second element.
And validate' :: (Integer, Integer) -> Maybe Integer with same logic, but returning tuple's second element.
validate (a, b) = greater (a, b) *> even b *> Just (a, b)
validate' (a, b) = greater (a, b) *> even b *> Just b
But I imagine that the input tuple "flows" into greater, then "flows" into some kind of composition of snd and even and then only single element ends up in the final Just.
What would a haskeller do?
When you are writing validators of the form
a -> Maybe byou are more interested in that whole type than in theMaybeapplicative. The typea -> Maybe bare the Kleisli arrows of theMaybemonad. You can make some tools to help work with this type.For the first question you can define
and write
Your second examples are
These check the conditions in a different order. If you care about evaluation order you can define another function
<*<.ReaderT
If you use the type
a -> Maybe ba lot it might be worth creating anewtypefor it so that you can add your own instances for what you want it to do. Thenewtypealready exists; it'sReaderT, and its instances already do what you want to do.When you use the type
r -> Maybe aas a validator to validate and transform a single inputrit's the same asReaderT r Maybe. TheApplicativeinstance forReaderTcombines two of them together by applying both of their functions to the same input and then combining them together with<*>:ReaderT's<*>is almost exactly the same as>*>from the first section, but it doesn't discard the first result.ReaderT's*>is exactly the same as>*>from the first section.In terms of
ReaderTyour examples becomeand
You use one of these
ReaderTvalidators on a valuexbyrunReaderT validate x