KISS 🇺🇦

Stop the war!

Stop the war in Ukraine! Fuck putin!

More information is at: https://war.ukraine.ua/.

There is a fund to support the Ukrainian Army: https://savelife.in.ua/en/donate/, and there is a special bank account that accepts funds in multiple currencies: https://bank.gov.ua/en/about/support-the-armed-forces. I donated to them. Please donate if you can!

Killer putin

Killer putin. Source: politico.eu.

Arrested putin

"It hasn't happened yet, but it will happen sooner or later. Beautiful photo, isn't it?" Source: twitter.

Details about a Haskell puzzle

| comments

I’ve seen this “Haskell Puzzles” https://jaspervdj.be/posts/2023-06-19-haskell-puzzles.html page on Haskell Weekly and decided to see what it was about. It has puzzles where you need to rearrange the given tokens so that the final expression produces the needed value.

The first puzzle was easy, and the second one… was hard. The tokens are iterate ) 5 join !! 1 ( (+) with the goal value of 32. The puzzle also says: “What Monad are we looking for?”. I thought, of course, [] since we have join and !!. I also saw that 5 and 32 were most likely related: 2^5 = 32, but I couldn’t see a way of generating powers of two here.

I spent a few hours on two evenings trying to arrange the tokens into something meaningful, but failed to put anything into iterate. After a pause, I recollected that function arrows are also monads! That’s the clue. It took me a few minutes to solve the puzzle after that. In order not to spoil the solution too much here, it contains join (+). Then I wanted to understand how it works. Starting at the beginning:

1
join :: Monad m => m (m x) -> m x

Note: the following code is wrong!

1
2
3
4
5
6
7
8
9
instance Monad (-> r)

(-> r) a = a -> r

-- m ~ (-> r)
join :: ((-> r) ((-> r) a)) -> ((-> r) a)
join :: ((-> r) a) -> r -> (a -> r)
join :: a -> r -> r -> a -> r
join :: ((a -> r) -> r) -> (a -> r)

It’s wrong because I missed the parentheses around the arrow, which matters a lot:

1
2
3
4
5
6
7
8
9
10
instance Monad ((->) r)

a -> b = (->) a b

-- m ~ ((->) r)
join :: ( ((->) r) ( ((->) r) a ) ) -> ( (-> r) a )
( (-> r) a ) = r -> a
( ((->) r) ( ((->) r) a ) ) = ( ((->) r) (r -> a) ) = r -> (r -> a) = r -> r -> a
join :: (r -> r -> a) -> (r -> a)
join :: (r -> r -> a) -> r -> a

This is confirmed by ghci:

1
2
λ> :t join @((->) _)
join @((->) _) :: Monad ((->) w) => (w -> (w -> a)) -> w -> a

And here is how the function arrow behaves just like any other operator:

1
2
3
4
5
6
λ> :t (!!) :: ([a] -> (Int -> a))
(!!) :: ([a] -> (Int -> a)) :: [a] -> Int -> a
λ> :t (!!) :: ((->) [a] (Int -> a))
(!!) :: ((->) [a] (Int -> a)) :: [a] -> (Int -> a)
λ> :t (!!) :: (((->) [a]) (Int -> a))
(!!) :: (((->) [a]) (Int -> a)) :: [a] -> (Int -> a)

Applying join (+) to iterate:

1
2
3
4
5
6
7
8
9
10
11
(+) :: x -> x -> x

-- substituting (r ~ x, a ~ x) in the `join` above
join (+) :: x -> x

-- what `iterate` calculates, from the documentation
iterate :: (a -> a) -> a -> [a]
iterate f x = [x, f x, f $ f x, f $ f $ f x, ]

iterate (join (+)) :: x -> [x]
iterate (join (+)) x = [x, join (+) $ x, join (+) $ join (+) $ x, ]

The types work out here, which is great… but what does join (+) actually do?! I can use equational reasoning to figure out the definitions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- Monad is usually defined via `(>>=)`
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(>>=) :: (r -> a) -> (a -> r -> b) -> (r -> b)
ra >>= arb = \r -> let a = ra r in arb a r

join mma = mma >>= \ma -> ma = mma >>= id

join (+)
  = (+) >>= id
  = \r -> let a = (+) r in id a r
  = \r -> let a = (+ r) in id a r
  = \r -> id (+ r) r
  = \r -> (+ r) r
  = \r -> r + r

Turns out, join (+) takes a number and adds it to itself! Therefore:

1
2
3
4
5
6
7
8
9
10
11
12
iterate (join (+)) 1 :: [Int]
iterate (join (+)) 1 =
  [ 1
  , 1+1 = 2
  , (1+1) + (1+1) = 4
  , ((1+1) + (1+1)) + ((1+1) + (1+1)) = 8
  , (((1+1) + (1+1)) + ((1+1) + (1+1))) + (((1+1) + (1+1)) + ((1+1) + (1+1))) = 16
  , 32
  , 
  ]

iterate (join (+)) 1 !! 5 = 32

The Monad “we were looking for” is not [], but (->) r in this case! This was fun!

https://eli.thegreenplace.net/2018/haskell-functions-as-functors-applicatives-and-monads/ has more details about function arrows as functors, applicatives and monads.

Comments