Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a function be "transparently augmented" in Haskell?

Tags:

haskell

Situation

I have function f, which I want to augment with function g, resulting in function named h.

Definitions

By "augment", in the general case, I mean: transform either input (one or more arguments) or output (return value) of function f.

By "augment", in the specific case, (specific to my current situation) I mean: transform only the output (return value) of function f while leaving all the arguments intact.

By "transparent", in the context of "augmentation", (both the general case and the specific case) I mean: To couple g's implementation as loosely to f's implementation as possible.

Specific case

In my current situation, this is what I need to do:

h a b c = g $ f a b c

I am interested in rewriting it to something like this:

h = g . f -- Doesn't type-check.

Because from the perspective of h and g, it doesn't matter what arguments f take, they only care about the return value, hence it would be tight coupling to mention the arguments in any way. For instance, if f's argument count changes in the future, h will also need to be changed.

So far

I asked lambdabot on the #haskell IRC channel: @pl h a b c = g $ f a b c to which I got the response:

h = ((g .) .) . f

Which is still not good enough since the number of (.)'s is dependent on the number of f's arguments.

General case

I haven't done much research in this direction, but erisco on #haskell pointed me towards http://matt.immute.net/content/pointless-fun which hints to me that a solution for the general case could be possible.

So far

Using the functions defined by Luke Palmer in the above article this seems to be an equivalent of what we have discussed so far:

h = f $. id ~> id ~> id ~> g

However, it seems that this method sadly also suffers from being dependent on the number of arguments of f if we want to transform the return value of f -- just as the previous methods.

Working example

In JavaScript, for instance, it is possible to achieve transparent augmentation like this:

function h () { return g(f.apply(this, arguments)) }

Question

How can a function be "transparently augmented" in Haskell?

I am mainly interested in the specific case, but it would be also nice to know how to handle the general case.

like image 557
Wizek Avatar asked Dec 20 '22 15:12

Wizek


1 Answers

You can sort-of do it, but since there is no way to specify a behavior for everything that isn't a function, you'll need a lot of trivial instances for all the other types you care about.

{-# LANGUAGE TypeFamilies, DefaultSignatures #-}

class Augment a where
  type Result a
  type Result a = a

  type Augmented a r
  type Augmented a r = r

  augment :: (Result a -> r) -> a -> Augmented a r

  default augment :: (a -> r) -> a -> r
  augment g x = g x

instance Augment b => Augment (a -> b) where
  type Result (a -> b) = Result b
  type Augmented (a -> b) r = a -> Augmented b r

  augment g f x = augment g (f x) 

instance Augment Bool
instance Augment Char
instance Augment Integer
instance Augment [a]

-- and so on for every result type of every function you want to augment...

Example:

> let g n x ys = replicate n x ++ ys
> g 2 'a' "bc"
"aabc"
> let g' = augment length g
> g' 2 'a' "bc"
4
> :t g
g :: Int -> a -> [a] -> [a]
> :t g'
g' :: Int -> a -> [a] -> Int
like image 122
hammar Avatar answered Jan 23 '23 07:01

hammar