Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to define patterns of composition in functions or functors?

Consider the following situation. I define a function to process a list of elements by the typical way of doing an operation on the head and recalling the function over the rest of the list. But under certain condition of the element (being negative, being a special character, ...) I change sign on the rest of the list before continuing. Like this:

f [] = []
f (x : xs) 
    | x >= 0      = g x : f xs
    | otherwise   = h x : f (opposite xs)

opposite [] = []
opposite (y : ys) = negate y : opposite ys

As opposite (opposite xs) = xs, I become to the situation of redundant opposite operations, accumulating opposite . opposite . opposite ....

It happens with other operations instead of opposite, any such that the composition with itself is the identity, like reverse.

Is it possible to overcome this situation using functors / monads / applicatives / arrows? (I don't understand well those concepts). What I would like is to be able of defining a property, or a composition pattern, like this:

opposite . opposite  = id    -- or, opposite (opposite y) = y

in order that the compiler or interpreter avoids to calculate the opposite of the opposite (it is possible and simple (native) in some concatenative languages).

like image 329
enrique Avatar asked Dec 25 '22 13:12

enrique


2 Answers

You can solve this without any monads, since the logic is quite simple:

f g h = go False where 
  go _ [] = [] 
  go b (x':xs)
    | x >= 0    = g x : go b xs 
    | otherwise = h x : go (not b) xs
      where x = (if b then negate else id) x'

The body of the go function is almost identical to that of your original f function. The only difference is that go decides whether the element should be negated or not based on the boolean value passed to it from previous calls.

like image 192
user2407038 Avatar answered May 06 '23 03:05

user2407038


Sure, just keep a bit of state telling whether to apply negate to the current element or not. Thus:

f = mapM $ \x_ -> do
    x <- gets (\b -> if b then x_ else negate x_)
    if x >= 0
        then return (g x)
        else modify not >> return (h x)
like image 22
Daniel Wagner Avatar answered May 06 '23 03:05

Daniel Wagner