Convert an operator to type `t` to an operator of type` a-> t`
Is this the standard way to convert operator by type t
to operator by type a->t
? Ie, this is a lib implementing this function ( fT
as functionTransformer
):
fT :: (t -> t -> t) -> (a -> t) -> (a -> t) -> (a -> t)
fT op f1 f2 = \x -> (f1 x) `op` (f2 x)
(we could generalize to fT :: (t1 -> t2 -> t) -> (t3 -> t1) -> (t3 -> t2) -> t3 -> t
)
I asked this question while learning Yesod: in this framework, we can add a validation condition for a field thanks to checkBool
. For example, I can create a field that only accepts a value greater than 100 with:
smallIntField = checkBool (<= 100) "error: this entry has to be smaller than 100" intField
Thanks to my "functional transformer" I can easily control the limited value with
($&&) = fT (&&)
boundedIntField = checkBool ((>= 0) $&& (<= 100)) "error: this entry has to be between 0 and 100" intField
source to share
What you are really looking for are combinators liftA2
or liftM2
(from Control.Applicative
or Control.Monad
respectively). liftM2
will work the same way liftA2
as all monads are applicative, but it's up to you if you want to be more or less restrictive. Using a monadic implementation will also force the order in which the arguments are evaluated, which cannot be guaranteed liftA2
.
You can use it like
(<&&>) :: Applicative f => f Bool -> f Bool -> f Bool
(<&&>) = liftA2 (&&)
For functions like (>= 0)
or (<= 100)
, Applicative f => f
specializes in (Ord a, Num a) => (->) a
, so the type signature would be
(<&&>) :: (Ord a, Num a) => (a -> Bool) -> (a -> Bool) -> (a -> Bool)
So, you can write something like
between :: Int -> Int -> Int -> Bool
between lower upper = (lower <=) <&&> (<= upper)
If you have defined <||> = liftM2 (||)
then you could do something more complex like
between 0 100 <||> between 200 400 <||> (>= 1000) :: Int -> Bool
To check if a number is an element [0, 100] U [200, 400] U [1000, inf)
, if we write it in the notation set ( U
union is established).
source to share
This code is in the module Control.Applicative
:
instance Applicative ((->) r) where
pure a = \_ -> a
ff <*> fa = \r -> ff r (fa r)
liftA2 :: Applicative f => f (a -> b -> c) -> f a -> f b -> f c
liftA2 f fa fb = pure f <*> fa <*> fb
Your function fT
is what we get when we take liftA2
and use (->) a
for f
and t
for a
, b
and c
. We can rewrite your function like this:
fT :: (t -> t -> t) -> (a -> t) -> (a -> t) -> (a -> t)
fT = liftA2
By substitution, we can prove it just like yours:
liftA2 op f1 f2
= (pure op <*> f1) <*> f2
= \a -> (pure op <*> f1) a (f2 a)
= \a -> (\a -> pure op a (fa 1)) a (f2 a)
= \a -> (\a -> (\_ -> op) a (f1 a)) a (f2 a)
= \a -> (\a -> op (f1 a)) a (f2 a)
= \a -> op (f1 a) (f2 a)
= \a -> (f1 a) `op` (f2 a)
source to share