[Haskell-cafe] Have you seen this functor/contrafunctor combo?

Edward Kmett ekmett at gmail.com
Sun Jun 10 00:28:39 CEST 2012


Here is a considerably longer worked example using the analogy to J,
borrowing heavily from Wadler:

As J, this doesn't really add any power, but perhaps when used with
non-representable functors like Equivalence/Comparison you can do something
more interesting.

-- Used for Hilbert
{-# LANGUAGE DefaultSignatures, TypeOperators #-}

-- Used for Representable
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleContexts,
FlexibleInstances #-}

module Search where

import Control.Applicative
import Data.Function (on)
import Data.Functor.Contravariant
import GHC.Generics -- for Hilbert

newtype Search f a = Search { optimum :: f a -> a }

instance Contravariant f => Functor (Search f) where
  fmap f (Search g) = Search $ f . g . contramap f

instance Contravariant f => Applicative (Search f) where
 pure a = Search $ \_ -> a
 Search fs <*> Search as = Search $ \k ->
   let go f = f (as (contramap f k))
   in  go (fs (contramap go k))

instance Contravariant f => Monad (Search f) where
  return a = Search $ \_ -> a
  Search ma >>= f = Search $ \k ->
    optimum (f (ma (contramap (\a -> optimum (f a) k) k))) k

class Contravariant f => Union f where
  union :: Search f a -> Search f a -> Search f a

instance Union Predicate where
  union (Search ma) (Search mb) = Search $ \ p -> case ma p of
    a | getPredicate p a -> a
      | otherwise        -> mb p

instance Ord r => Union (Op r) where
  union (Search ma) (Search mb) = Search $ \ f -> let
      a = ma f
      b = mb f
    in if getOp f a >= getOp f b then a else b

both :: Union f => a -> a -> Search f a
both = on union pure

fromList :: Union f => [a] -> Search f a
fromList = foldr1 union . map return

class Contravariant f => Neg f where
  neg :: f a -> f a

instance Neg Predicate where
  neg (Predicate p) = Predicate (not . p)

instance Num r => Neg (Op r) where
  neg (Op f) = Op (negate . f)

pessimum :: Neg f => Search f a -> f a -> a
pessimum m p = optimum m (neg p)

forsome :: Search Predicate a -> (a -> Bool) -> Bool
forsome m p = p (optimum m (Predicate p))

forevery :: Search Predicate a -> (a -> Bool) -> Bool
forevery m p = p (pessimum m (Predicate p))

member :: Eq a => a -> Search Predicate a -> Bool
member a x = forsome x (== a)

each :: (Union f, Bounded a, Enum a) => Search f a
each = fromList [minBound..maxBound]

bit :: Union f => Search f Bool
bit = fromList [False,True]

cantor :: Union f => Search f [Bool]
cantor = sequence (repeat bit)

least :: (Int -> Bool) -> Int
least p = head [ i | i <- [0..], p i ]

infixl 4 -->
(-->) :: Bool -> Bool -> Bool
p --> q = not p || q

fan :: Eq r => ([Bool] -> r) -> Int
fan f = least $ \ n ->
          forevery cantor $ \x ->
            forevery cantor $ \y ->
              (take n x == take n y) --> (f x == f y)

-- a length check that can handle infinite lists
compareLength :: [a] -> Int -> Ordering
compareLength xs n = case drop (n - 1) xs of
   []  -> LT
   [_] -> EQ
   _   -> GT

-- Now, lets leave Haskell 98 behind

-- Using the new GHC generics to derive versions of Hilbert's epsilon

class GHilbert t where
  gepsilon :: Union f => Search f (t a)

class Hilbert a where
  -- http://en.wikipedia.org/wiki/Epsilon_calculus
  epsilon :: Union f => Search f a
  default epsilon :: (Union f, GHilbert (Rep a), Generic a) => Search f a
  epsilon = fmap to gepsilon

instance GHilbert U1 where
  gepsilon = return U1

instance (GHilbert f, GHilbert g) => GHilbert (f :*: g) where
  gepsilon = liftA2 (:*:) gepsilon gepsilon

instance (GHilbert f, GHilbert g) => GHilbert (f :+: g) where
  gepsilon = fmap L1 gepsilon `union` fmap R1 gepsilon

instance GHilbert a => GHilbert (M1 i c a) where
  gepsilon = fmap M1 gepsilon

instance Hilbert a => GHilbert (K1 i a) where
  gepsilon = fmap K1 epsilon

instance Hilbert ()
instance (Hilbert a, Hilbert b) => Hilbert (a, b)
instance (Hilbert a, Hilbert b, Hilbert c) => Hilbert (a, b, c)
instance (Hilbert a, Hilbert b, Hilbert c, Hilbert d) =>
  Hilbert (a, b, c, d)
instance (Hilbert a, Hilbert b, Hilbert c, Hilbert d, Hilbert e) =>
  Hilbert (a, b, c, d, e)
instance Hilbert Bool
instance Hilbert Ordering
instance Hilbert a => Hilbert [a]
instance Hilbert a => Hilbert (Maybe a)
instance (Hilbert a, Hilbert b) => Hilbert (Either a b)
instance Hilbert Char where
  epsilon = each
instance (Union f, Hilbert a) => Hilbert (Search f a) where
  epsilon = fmap fromList epsilon

search :: (Union f, Hilbert a) => f a -> a
search = optimum epsilon

find :: Hilbert a => (a -> Bool) -> a
find = optimum epsilon . Predicate

every :: Hilbert a => (a -> Bool) -> Bool
every = forevery epsilon

exists :: Hilbert a => (a -> Bool) -> Bool
exists = forsome epsilon

-- and MPTCs/Fundeps to define representable contravariant functors:

class Contravariant f => Representable f r | f -> r where
  represent :: f a -> a -> r
  tally     :: (a -> r) -> f a

instance Representable (Op r) r where
  represent (Op f) = f
  tally = Op

instance Representable Predicate Bool where
  represent (Predicate p) = p
  tally = Predicate

supremum :: Representable f r => Search f a -> (a -> r) -> r
supremum m p = p (optimum m (tally p))

infimum :: (Representable f r, Neg f) => Search f a -> (a -> r) -> r
infimum m p = p (pessimum m (tally p))

A few toy examples:

ghci> supremum (fromList [1..10] :: Search (Op Int) Int) id
10
ghci> find (=='a')
'a'
ghci> fan (!!4)
5
ghci> find (\xs -> compareLength xs 10 == EQ && (xs !! 4) == 'a')
"\NUL\NUL\NUL\NULa\NUL\NUL\NUL\NUL\NUL"

-Edward
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20120609/f3f8d7db/attachment.htm>


More information about the Haskell-Cafe mailing list