Skip to content

Instantly share code, notes, and snippets.

@frasertweedale
Last active October 6, 2021 21:45
Show Gist options
  • Save frasertweedale/3727fec7c19ed6b62d60037cce679c76 to your computer and use it in GitHub Desktop.
Save frasertweedale/3727fec7c19ed6b62d60037cce679c76 to your computer and use it in GitHub Desktop.
length-indexed list experiment
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
import Control.Applicative (Alternative(..))
import Data.Char (isDigit)
newtype Const a b = Const { getConst :: a }
newtype Flip p a b = Flip { runFlip :: p b a }
newtype Compose f g a = Compose { runCompose :: f (g a) }
data Nat = Z | S Nat
type family SUB (n :: Nat) (m :: Nat) :: Nat
type instance SUB n Z = n
type instance SUB (S n) (S m) = SUB n m
class NatInd (n :: Nat) where
ind :: f Z -> (forall m. NatInd m => f m -> f (S m)) -> f n
instance NatInd Z where
ind z _f = z
instance (NatInd m) => NatInd (S m) where
ind z f = f (ind z f)
type ZERO = Z
type ONE = S Z
type TWO = S (S Z)
type THREE = S (S (S Z))
type FOUR = S (S (S (S Z)))
type FIVE = S (S (S (S (S Z))))
type SIX = S (S (S (S (S (S Z)))))
type SEVEN = S (S (S (S (S (S (S Z))))))
type EIGHT = S (S (S (S (S (S (S (S Z)))))))
data EXACTLY (n :: Nat) a where
NNil :: EXACTLY Z a
NCons :: a -> EXACTLY n a -> EXACTLY (S n) a
infixr 5 `NCons`
instance Functor (EXACTLY n) where
fmap f l = case l of
NNil -> NNil
NCons x xs -> NCons (f x) (fmap f xs)
instance (Show a) => Show (EXACTLY n a) where
show = show . toListExact
toListExact :: EXACTLY n a -> [a]
toListExact l = case l of
NNil -> []
NCons h t -> h : toListExact t
fromListExact :: NatInd n => [a] -> Maybe (EXACTLY n a)
fromListExact = fmap (fmap runFlip . runCompose) . runCompose $ ind z f
where
z = Compose $ \l -> Compose $ case l of
[] -> Just (Flip NNil)
_ -> Nothing
f r = Compose $ \l -> Compose $ case l of
[] -> Nothing
(x:xs) -> fmap (Flip . NCons x . runFlip) . runCompose $ runCompose r xs
data UPTO (n :: Nat) a where
UNil :: UPTO n a
UCons :: a -> UPTO n a -> UPTO (S n) a
infixr 5 `UCons`
instance Functor (UPTO n) where
fmap f l = case l of
UNil -> UNil
UCons x xs -> UCons (f x) (fmap f xs)
instance (Show a) => Show (UPTO n a) where
show = show . toListUpto
toListUpto :: UPTO n a -> [a]
toListUpto l = case l of
UNil -> []
UCons h t -> h : toListUpto t
fromListUpto :: NatInd n => [a] -> Maybe (UPTO n a)
fromListUpto = fmap (fmap runFlip . runCompose) . runCompose $ ind z f
where
z = Compose $ \l -> Compose $ case l of
[] -> Just (Flip UNil)
_ -> Nothing
f r = Compose $ \l -> Compose $ case l of
[] -> Just (Flip UNil)
(x:xs) -> fmap (Flip . UCons x . runFlip) . runCompose $ runCompose r xs
{- LIST OF SIZE IN RANGE, REUSING EXACTLY AND UPTO -}
data FROMTO (lo :: Nat) (hi :: Nat) a
= FROMTO (EXACTLY lo a) (UPTO (SUB hi lo) a)
instance Functor (FROMTO lo hi) where
fmap f (FROMTO xs ys) = FROMTO (fmap f xs) (fmap f ys)
toListFromto :: FROMTO lo hi a -> [a]
toListFromto (FROMTO xs ys) = toListExact xs <> toListUpto ys
instance Show a => Show (FROMTO lo hi a) where
show = show . toListFromto
fromListFromto
:: forall lo hi a. (NatInd lo, NatInd (SUB hi lo))
=> [a] -> Maybe (FROMTO lo hi a)
fromListFromto l =
let
i = getConst (ind (Const 0) (Const . (+1) . getConst) :: Const Int lo)
(l1, l2) = (take i l, drop i l)
in
FROMTO <$> fromListExact l1 <*> fromListUpto l2
-- | Lock-step induction over two nats
class NatInd2 (n :: Nat) (m :: Nat) where
ind2
:: f Z Z
-> (forall q. (NatInd2 Z q) => f Z q -> f Z (S q))
-> (forall p. (NatInd2 p Z) => f p Z -> f (S p) Z)
-> (forall p q. (NatInd2 p q) => f p q -> f (S p) (S q))
-> f n m
instance NatInd2 Z Z where
ind2 zz _zm _nz _nm = zz
instance (NatInd2 Z m) => NatInd2 Z (S m) where
ind2 zz zm nz nm = zm (ind2 zz zm nz nm)
instance (NatInd2 n Z) => NatInd2 (S n) Z where
ind2 zz zm nz nm = nz (ind2 zz zm nz nm)
instance (NatInd2 n m) => NatInd2 (S n) (S m) where
ind2 zz zm nz nm = nm (ind2 zz zm nz nm)
{- LIST OF SIZE IN RANGE, STRUCTURAL -}
data RANGE (lo :: Nat) (hi :: Nat) a where
RNil :: RANGE Z hi a
RCons :: a -> RANGE lo hi a -> RANGE (S lo) (S hi) a
RConsHi :: a -> RANGE lo hi a -> RANGE lo (S hi) a
infixr 5 `RCons`
infixr 5 `RConsHi`
instance Functor (RANGE lo hi) where
fmap f l = case l of
RNil -> RNil
RCons x xs -> RCons (f x) (fmap f xs)
RConsHi x xs -> RConsHi (f x) (fmap f xs)
instance (Show a) => Show (RANGE lo hi a) where
show = show . toListRange
toListRange :: RANGE lo hi a -> [a]
toListRange l = case l of
RNil -> []
RCons h t -> h : toListRange t
RConsHi h t -> h : toListRange t
newtype Flip2 p a b c = Flip2 { runFlip2 :: p b c a }
newtype Compose2 f g a b = Compose2 { runCompose2 :: f (g a b) }
fromListRange :: (NatInd2 lo hi) => [a] -> Maybe (RANGE lo hi a)
fromListRange = fmap (fmap runFlip2 . runCompose2) . runCompose2 $ ind2
-- lo and hi reached zero
-- if input empty, return empty list
-- if input non-empty, fail
( Compose2 $ \l -> Compose2 $ case l of [] -> Just (Flip2 RNil) ; _ -> Nothing )
-- lo reached zero
-- if input empty, return empty list
-- if input non-empty, recurse
( \r -> Compose2 $ \l -> Compose2 $ case l of
[] -> Just (Flip2 RNil)
(x:xs) -> fmap (Flip2 . RConsHi x . runFlip2) . runCompose2 $ runCompose2 r xs )
-- hi cannot reach zero before lo; fail
( \_r -> Compose2 $ \_l -> Compose2 Nothing )
-- hi and low non-zero
-- if input empty, fail
-- if input non-empty, recurse
( \r -> Compose2 $ \l -> Compose2 $ case l of
[] -> Nothing
(x:xs) -> fmap (Flip2 . RCons x . runFlip2) . runCompose2 $ runCompose2 r xs )
{- PARSER -}
newtype Parser a = Parser { runParser :: String -> Maybe (a, String) }
parseFailure :: Parser a
parseFailure = Parser $ const Nothing
satisfy :: (Char -> Bool) -> Parser Char
satisfy pred = Parser $ \s -> case s of
(c:cs) | pred c -> Just (c, cs)
_ -> Nothing
char :: Char -> Parser Char
char c = satisfy (== c)
digit :: Parser Char
digit = satisfy isDigit
instance Functor Parser where
fmap f p = Parser $ \s -> case runParser p s of
Nothing -> Nothing
Just (a, s') -> Just (f a, s')
instance Applicative Parser where
pure a = Parser $ \s -> Just (a, s)
pf <*> pa = Parser $ \s -> case runParser pf s of
Nothing -> Nothing
Just (f, s') -> runParser (f <$> pa) s'
instance Alternative Parser where
empty = parseFailure
p1 <|> p2 = Parser $ \s -> runParser p1 s <|> runParser p2 s
parseRange :: (NatInd2 lo hi) => Parser a -> Parser (RANGE lo hi a)
parseRange p = fmap runFlip2 . runCompose2 $ ind2
( Compose2 . pure . Flip2 $ RNil )
-- lo reached zero, hi did not.
-- continue parsing, failure is acceptable
--
( \r -> Compose2 . fmap Flip2 $
RConsHi <$> p <*> (fmap runFlip2 . runCompose2 $ r)
<|> pure RNil
)
( \_r -> Compose2 parseFailure )
-- hi and low non-zero
-- continue parsing, failure at this level unacceptable
( \r -> Compose2 . fmap Flip2 $
RCons <$> p <*> (fmap runFlip2 . runCompose2 $ r)
)
@LightAndLight
Copy link

LightAndLight commented Oct 6, 2021

I think FROMTO can be similarly improved. I'm running out of steam, so I'll just leave this here:

data FROMTO : Nat -> Nat -> Type -> Type where
  FROMTO : lo <= m -> m <= hi -> EXACTLY m a -> FROMTO lo hi a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment