>:t head
head :: [a] -> a
> head [1,2,3]
1
> head []
*** Exception: Prelude.head: empty list
> head []
-1
head :: [a] -> a
headMaybe :: [a] -> Maybe a
headMaybe (x:xs) = Just x
headMaybe [] = Nothing
data NonEmpty a = a :| [a]
safeHead :: NonEmpty a -> a
safeHead (x :| xs) = x
Our choice:
People will pass responsibility in the same direction the code they call does.
When we restrict what we can do, it’s easier to understand what we can do.
identity :: a -> a
take :: Int -> [a] -> [a]
length :: [a] -> Int
-- merge 2 already sorted lists
unsafeMergeBy
:: (a -> a -> Ordering) -- the comparator
-> [a] -- first list
-> [a] -- second list
-> [a] -- merged list
> x = sortBy compare [4,3,1]
> y = sortBy compare [16,5,6]
> x
[1,3,4]
> y
[5,6,16]
> unsafeMergeBy compare x y
[1,3,4,5,6,16]
> unsafeMergeBy (comparing Down) x y
[5,6,16,1,3,4]
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi nec metus justo. Aliquam erat volutpat.
-- merge 2 already sorted lists
mergeByMaybe
:: (a -> a -> Ordering) -- the comparator
-> [a]
-> [a]
-- a merged list
-- or none if the list passed
-- was not sorted
-> Maybe [a]
> x = sortBy compare [4,3,1]
> y = sortBy compare [16,5,6]
> fromJust $ mergeByMaybe compare x y
[1,3,4,5,6,16]
newtype Named name a = Named a
type a ~~ name = Named name a
name :: a -> (forall name. a ~~ name -> t) -> t
name x k = k (coerce x)
class The d a | d -> a where
the :: d -> a
default the :: Coercible d a => d -> a
the = coerce
newtype Named name a = Named a
type a ~~ name = Named name a
class The d a | d -> a where
the :: d -> a
default the :: Coercible d a => d -> a
the = coerce
name :: a -> (forall name. a ~~ name -> t) -> t
name x k = k (coerce x)
newtype SortedBy name a = SortedBy a
instance The (SortedBy name a) a
import qualified Lists as L
sortBy :: ((a -> a -> Ordering) ~~ comp)
-> [a]
-> SortedBy comp [a]
sortBy comp xs = coerce (L.sortBy (the comp) xs)
mergeBy :: ((a -> a -> Ordering) ~~ comp)
-> SortedBy comp [a]
-> SortedBy comp [a]
-> SortedBy comp [a]
mergeBy comp xs ys =
coerce (L.mergeBy (the comp) (the xs) (the ys))
> name compare $ \gt -> do
let xs' = sortBy gt xs
ys' = sortBy gt ys
print (the (mergeBy gt xs' ys'))
> x = sortBy compare [4,3,1]
> y = sortBy compare [16,5,6]
> unsafeMergeBy (comparing Down) x y
[5,6,16,1,3,4]
> x = sortBy compare [4,3,1]
> y = sortBy compare [16,5,6]
> fromJust $ mergeByMaybe (comparing Down) x y
*** Exception: Maybe.fromJust: Nothing
> name compare $ \gt -> do
let xs' = sortBy gt xs
ys' = sortBy gt ys
print (the (mergeBy gt xs' ys'))
minimum_O1 :: SortedBy comp [a] -> Maybe a
minimum_O1 xs = case the xs of
[] -> Nothing
(x:_) -> Just x
data Proof p = QED
axiom :: String -> Proof p
axiom reason = QED
newtype Rev xs = Rev ()
rev_rev
:: Proof (Rev (Rev xs) == xs)
rev_rev =
axiom "reverse reverse is identity"
reverse
:: ([a] ~~ xs)
-> ([a] ~~ Rev xs)
reverse xs =
coerce (P.reverse (the xs))
data p == q
Use existential names to discuss values at the type level
No runtime overhead
Give user combinators and proofs to construct their own safety arguments
Is it useful?
maybe, it's one approach
Use existential names to discuss values at the type level
No runtime overhead
Give user combinators and proofs to construct their own safety arguments
Is it useful?
maybe, it's one approach
newtype Rev xs = Rev ()
reverse
:: ([a] ~~ xs)
-> ([a] ~~ Rev xs)
reverse xs =
coerce (P.reverse (the xs))
rev_rev
:: Proof (Rev (Rev xs) == xs)
rev_rev =
axiom "reverse reverse is identity"
data Proof p = QED
axiom :: Proof p
axiom = QED
data p || q
data p && q
data p == q
andElimL
:: Proof (p && q)
-> Proof p
or_introL
:: p
-> Proof (p || q)
-- sitting in phantom type variables- no constructors needed
data IsCons xs
data IsNil xs
pattern IsCons
:: Proof (IsCons xs)
-> ([a] ~~ xs)
pattern IsNil
:: Proof (IsNil xs)
-> ([a] ~~ xs)
head
:: ([a] ~~ xs ::: IsCons xs)
-> a
head xs =
Prelude.head (the xs)
name [1,3] $ \xs -> case xs of
IsCons proof ->
print (head (xs ...proof))
IsNil proof -> print “nada”
rev_cons
:: Proof (IsCons xs)
-> Proof (IsCons (Reverse xs))
rev_cons _ = axiom
name xs $ \xs -> case xs of
IsCons proof ->
print (head (xs ...proof))
print (head (reverse xs ...rev_cons proof))
newtype Reverse xs
= Reverse Defn
reverse
:: ([a] ~~ xs)
-> ([a] ~~ Reverse xs)
reverse xs =
defn
(Prelude.reverse (the xs))
How do we handle props involving multiple values, eg relationships between values?