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

      

+3


source to share


2 answers


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).

+6


source


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)

      

+2


source







All Articles