The example you give does work. Let's explain why:
three f = f . f . f
-- so...
three :: (a -> a) -> a -> a
The function needs to have type a -> a
because it will receive it's own argument, which requires a type. (2+)
has type Num a => a -> a
, so three (2+) 4
will work just fine.
However, when you pass a function like return
of type Monad m => a -> m a
, which returns a different type, it will not match the (a -> a)
requirement we set out. This is where and when your function will fail.
While you're at it, try making a function like doTimes
with type Integer -> (a -> a) -> a -> a
which does the given function the given number of times - it's a good next step after making this function.