packages feed

gdp-0.0.0.1: app/Main.hs

{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs         #-}

module Main where

import GDP

import Data.Ord
import qualified Data.List as L

-- An unsafe merge. This relies on the user remembering to
-- sort both of the inputs using the same comparator passed
-- as the first argument to `unsafeMergeBy`. Otherwise, it
-- will produce nonsense.
unsafeMergeBy :: (a -> a -> Ordering) -> [a] -> [a] -> [a]
unsafeMergeBy comp xs ys = go xs ys
  where
    go [] ys' = ys'
    go xs' [] = xs'
    go (x:xs') (y:ys') = case comp x y of
      GT -> y : go (x:xs') ys'
      _  -> x : go xs' (y:ys')


-- Introduce a predicate `SortedBy comp`, indicating that
-- the value has been sorted by the comparator named `comp`.
newtype SortedBy comp name = SortedBy Defn

-- Sort a value using the comparator named `comp`. The
-- resulting value will satisfy `SortedBy comp`.
sortBy :: ((a -> a -> Ordering) ~~ comp)
       -> [a]
       -> ([a] ?SortedBy comp)
sortBy (The comp) xs = assert (L.sortBy comp xs)

-- Merge the two lists using the comparator named `comp`. The lists must
-- have already been sorted using `comp`, and the result will also be
-- sorted with respect to `comp`.
mergeBy :: ((a -> a -> Ordering) ~~ comp)
        -> ([a] ?SortedBy comp)
        -> ([a] ?SortedBy comp)
        -> ([a] ?SortedBy comp)
mergeBy (The comp) (The xs) (The ys) = assert (unsafeMergeBy comp xs ys)

newtype Opposite comp = Opposite Defn

-- A named version of the opposite ordering.
opposite :: ((a -> a -> Ordering) ~~ comp)
         -> ((a -> a -> Ordering) ~~ Opposite comp)
opposite (The comp) = defn $ \x y -> case comp x y of
  GT -> LT
  EQ -> EQ
  LT -> GT

newtype Reverse xs = Reverse Defn

-- A named version of Prelude's 'reverse'.
rev :: ([a] ~~ xs) -> ([a] ~~ Reverse xs)
rev (The xs) = defn (reverse xs)

-- A lemma about reversing sorted lists.
rev_ord_lemma :: SortedBy comp xs -> Proof (SortedBy (Opposite comp) (Reverse xs))
rev_ord_lemma _ = axiom

-- Usage example.
main :: IO ()
main = do
  name compare $ \up -> do

    -- Read two lists and sort them in ascending order, then
    -- merge them and print the result.
    xs <- sortBy up <$> (readLn :: IO [Int])
    ys <- sortBy up <$> readLn
    let ans1 = mergeBy up xs ys
    print (the ans1)

    -- Now reverse the two lists and merge them using the
    -- descending comparator. This requires a proof that
    -- the reversed lists are sorted by the opposite of `up`,
    -- which we provide using (...?).
    let down = opposite up
        ans2 = mergeBy down (rev' xs) (rev' ys)
        rev' = rev ...? rev_ord_lemma
    print (the ans2)