Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't understand result when calling applyTwice multiple times

There are many questions based on applyTwice, but none that relate to my problem. I understand that the applyTwice function defined like this:

applyTwice :: (a -> a) -> a -> a
applyTwice f a = f (f a)

applies a function twice. So if I have an increment function:

increment x = x + 1

and do

applyTwice increment 0

I get 2. But I don't understand these results:

applyTwice applyTwice applyTwice increment 0 -- gives 16
applyTwice applyTwice applyTwice applyTwice increment 0 -- gives 65536
applyTwice applyTwice applyTwice applyTwice applyTwice increment 0 -- stack overflow

I also know that

twice = applyTwice applyTwice increment
applyTwice twice 0 -- gives 8

I simply cannot wrap my head around these results, would love it if someone could explain. I apologize if this is something basic, as I'm just learning Haskell.

like image 260
thehamzarocks Avatar asked Jul 11 '21 08:07

thehamzarocks


2 Answers

Let's use the informal notation

iter n f = f . f . f . ....  -- n times

Your applyTwice is then simply iter 2.

From the definition, we immediately get:

(iter n . iter m) f 
= iter n (iter m f)
= (f.f. ...) . ... . (f.f. ...)   -- n times (m times f)
= iter (n*m) f

hence, eta contracting,

iter n . iter m = iter (n*m)    -- [law 1]

We also have

iter n (iter m) 
=  -- definition
iter m . iter m . .... . iter m    -- n times
= -- law 1
iter (m*m* ... *m)                 -- n times
= -- power
iter (m^n)                         -- [law 2]

We then have, writing t for applyTwice:

t = iter 2

t t 
= -- previous equation
iter 2 (iter 2)
= -- law 2
iter (2^2)

t t t
= -- left associativity of application
(t t) t
= -- previous equation
iter (2^2) (iter 2)
= -- law 2
iter (2^(2^2))

t t t t
= -- left associativity of application
(t t t) t
= -- previous equation
iter (2^(2^2)) (iter 2)
= -- law 2
iter (2^(2^(2^2)))

and so on.

like image 52
chi Avatar answered Nov 06 '22 23:11

chi


There is a lot of complexity hiding behind the scenes in the form of invisible type arguments. What happens if we write these arguments out with -XTypeApplications. I have shortened some of the names.

The following twice is instantiated at twice @Int whose type is second-order.

one :: Int
one = twice (+ 1) 0
      ^^^^^
      | 
      twice @Int
        :: (Int -> Int) 
        -> (Int -> Int)

When you apply twice (order-3) to twice (order-2) the type signature becomes more complicated.

two :: Int
two = twice twice (+ 1) 0
      ^^^^^ ^^^^^
      |     |
      |     twice @Int
      |       :: (Int -> Int)
      |       -> (Int -> Int)
      |
      twice @(Int->Int)
        :: ((Int->Int) -> (Int->Int))
        -> ((Int->Int) -> (Int->Int))

And so forth, when you have twice twice twice they are order-4, order-3 and order-2 respectively:

three :: Int
three = twice twice twice (+ 1) 0
        ^^^^^ ^^^^^ ^^^^^
        |     |     | 
        |     |     twice @Int
        |     |       :: (Int -> Int)
        |     |       -> (Int -> Int)
        |     |
        |     twice @(Int->Int)
        |       :: ((Int->Int) -> (Int->Int))
        |       -> ((Int->Int) -> (Int->Int))
        |
        twice @((Int->Int)->(Int->Int))
          :: (((Int->Int)->(Int->Int)) -> ((Int->Int)->(Int->Int)))
          -> (((Int->Int)->(Int->Int)) -> ((Int->Int)->(Int->Int)))

the last example you gave becomes this monstrosity with order-6, order-5, order-4, order-3, order-2 respectively...

{-# Language TypeApplications #-}

five :: Int
five = twice @((((Int->Int)->(Int->Int))->((Int->Int)->(Int->Int)))->(((Int->Int)->(Int->Int))->((Int->Int)->(Int->Int))))
         (twice @(((Int->Int)->(Int->Int))->((Int->Int)->(Int->Int))))
         (twice @((Int->Int)->(Int->Int)))
         (twice @(Int->Int))
         (twice @Int)
         (+ 1) 0

So this is the type of the first twice!!

twice @((((Int->Int)->(Int->Int))->((Int->Int)->(Int->Int)))->(((Int->Int)->(Int->Int))->((Int->Int)->(Int->Int))))
  :: (((((Int -> Int) -> Int -> Int) -> (Int -> Int) -> Int -> Int)
       -> ((Int -> Int) -> Int -> Int) -> (Int -> Int) -> Int -> Int)
      -> (((Int -> Int) -> Int -> Int) -> (Int -> Int) -> Int -> Int)
      -> ((Int -> Int) -> Int -> Int)
      -> (Int -> Int)
      -> Int
      -> Int)
     -> ((((Int -> Int) -> Int -> Int) -> (Int -> Int) -> Int -> Int)
         -> ((Int -> Int) -> Int -> Int) -> (Int -> Int) -> Int -> Int)
     -> (((Int -> Int) -> Int -> Int) -> (Int -> Int) -> Int -> Int)
     -> ((Int -> Int) -> Int -> Int)
     -> (Int -> Int)
     -> Int
     -> Int
like image 27
Iceland_jack Avatar answered Nov 07 '22 00:11

Iceland_jack