Skip to content
Please note that GitHub no longer supports Internet Explorer.

We recommend upgrading to the latest Microsoft Edge, Google Chrome, or Firefox.

Learn more

Instantly share code, notes, and snippets.

@monadplus monadplus/fromNat.hs
Last active Feb 2, 2020

Embed
What would you like to do?
From Nat to SNat (singleton Nat)
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
module SNat where
-- Disclaimer: my knowledge in Haskell is very limited and this may not be 100% accurate.
--
-- **All credit to** https://github.com/i-am-tom/haskell-exercises from Tom Harding.
-- Also, shoutout to the book "Thinking with Types" from Sandy Maguire which explains this in detail.
data Nat = Z | S Nat
deriving Eq
instance Show Nat where
show = show . go 0
where
go acc Z = acc
go acc (S n) = go (acc + 1) n
data SNat (n :: Nat) where
SZ :: SNat 'Z
SS :: SNat n -> SNat ('S n)
toNat :: SNat n -> Nat
toNat SZ = Z
toNat (SS sn) = S (toNat sn)
-- This direction is tricky. Because we don't know n (it can be a runtime value)
fromNat :: (forall n. SNat n -> r) -> Nat -> r
fromNat f nat = f (fromNat' nat)
where
fromNat' :: Nat -> SNat n
fromNat' Z = SZ
fromNat' (S nat) = SS (fromNat' nat)
-- ^^^^^^^^^^^^^^ Compilation error
-- It will fail because n is "existential" (from the compiler POV).
-- n can never be defined outisde the rank-2 scope.
fromNat :: (forall n. SNat n -> r) -> Nat -> r
fromNat f Z = f SZ
fromNat f (S n) = fromNat (\some -> f (SS some)) n== x@!
testIso x = fromNat ((== x) . toNat) x
-- >>> let nat = S (S ( S( S Z)))
-- >>> testIso nat
-- A more compeling example
data Vector (n :: Nat) (a :: Type) where
VNil :: Vector 'Z a
VCons :: a -> Vector n a -> Vector ('S n) a
vlength :: forall n. Vector n a -> SNat n
vlength VNil = SZ
vlength (VCons _ v) = SS (vlength v)
-- This is similar to fromNat, we don't know the length of the vector
-- when we filter it because the compiler can't statically know which
-- elements are going to pass the predicate*
filterV :: (forall n. Vector n a -> r) -> (a -> Bool) -> Vector n a -> r
filterV f p VNil = f VNil
filterV f p (VCons a v) = filterV (\v' -> if p a then f (VCons a v') else f v') p v
-- >>> let v = VCons 1 (VCons 2 ( VCons 3 VNil))
-- Although we are running it forall n, the following fails:
--
-- >>> filterV (vlength) (< 2) v
--
-- • Couldn't match type ‘r’ with ‘SNat n1’
-- because type variable ‘n1’ would escape its scope
--
-- As I mentioned before, n can't scape the rank2 scope because it has been "existentialized"
-- from the compiler POV.
-- toNat removes the dependency on the forall n.
-- So we can finally know the length of the vector as it was a regular list.
--
-- >>> filterV (toNat . vlength) (< 2) v
-- 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.