Skip to content

Instantly share code, notes, and snippets.

@viercc
Created October 21, 2021 17:03
Show Gist options
  • Save viercc/38853067c893f7ad9e0894abb543178b to your computer and use it in GitHub Desktop.
Save viercc/38853067c893f7ad9e0894abb543178b to your computer and use it in GitHub Desktop.

Q: Is there an instance Monad ZipList compatible with Applicative ZipList?

A: No.

Proof.

Use these definitions

(<&>) :: Functor f => f a -> (a -> b) -> f b
(<&>) = flip fmap

empty :: ZipList a
empty = ZipList []

nat :: ZipList Integer
nat = ZipList [0..]

Use this lemma (proof skipped):

pure x = x <$ nat
join (nat <&> \i -> nat <&> \j -> f i j) = join (nat <$> \i -> f i i)
-- They are free variables
x :: Type
f :: Integer -> Integer -> x

q :: Integer -> ZipList (ZipList x)
q i = nat <&> \j -> if j < i then empty else pure (f i j)

ex :: ZipList (ZipList (ZipList x))
ex = nat <&> q

join . join $ ex
  = join . join $ nat <&> \i -> nat <&> \j -> if j < i then empty else pure (f i j)
  = join $ nat <&> \i -> if i < i then empty else pure (f i i)
  = join . fmap pure $ nat <&> \i -> f i i
  = nat <&> \i -> f i i

-- It must be:
join . fmap join $ ex = nat <&> \i -> f i i

fmap join ex = nat <&> \i -> join (q i)
-- By parametricity, join (q i) must keep its i-th element (f i i)

-- Generally, for any m >= 0:
join . join $ nat <&> \i -> q (i - m)
  = join . join $ nat <&> \i -> nat <&> \j -> if j < i - m then empty else pure (f (i - m) j)
  = join $ nat <&> \i -> pure (f (i - m) i)
  = nat <&> \i -> f (i - m) i

-- Thus it must be:
join . fmap join $ nat <&> \i -> q (i - m) = nat <&> \i -> f (i - m) i
-- By parametricity, join (q (i - m)) must not discard (f (i - m) i)
-- In other words, for every m >= 0, join (q i) must not discard the element (f i (i + m)).
-- No finite list can contain infinite number of elements, thus there should be
g :: Integer -> Integer -> x
-- Such that
join (q i) = nat <&> g i

-- The above implies
join . fmap join $ nat <&> \i -> q (i + 1)
  = join $ nat <&> \i -> join (q (i + 1))
  = join $ nat <&> \i -> nat <&> g (i + 1)
  = nat <&> g (i + 1) i

-- Then it contradicts to the monad law:
join . join $ nat <&> \i -> q (i + 1)
  = join . join $ nat <&> \i -> nat <&> \j -> if j < i + 1 then empty else pure (f (i + 1) j)
  = join $ nat <&> \i -> if i < i + 1 then empty else pure (f (i + 1) j)
  = join $ nat <&> \i -> empty
  = empty
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment