Henry Blanchette
Henry Blanchette

Reputation: 71

LiquidHaskell: Functor Law

I am encoding (as an assumption) the functor identity law like so:

...
import qualified Prelude ()
...


{-@ class Functor f where
      fmap :: forall a b. (a -> b) -> (f a -> f b) @-}
class Functor f where
  fmap :: (a -> b) -> (f a -> f b)


-- identity function
{-@ id :: forall a . x : a -> {y : a | x = y} @-}
id :: forall a . a -> a
id x = x

-- functor law: mapping with identity is identity
{-@ assume
    fmap_id :: forall f a . Functor f => x : f a ->
      {fmap id x = id x} @-}
fmap_id :: Functor f => f a -> ()
fmap_id _ = ()

I can't see anything wrong with this formulation, yet I get this error from LH:

src/Category/Functor/LH.hs:45:16: error:
    • Illegal type specification for `Category.Functor.LH.fmap_id`
    Category.Functor.LH.fmap_id :: forall f a .
                                   (Functor<[]> f) =>
                                   x:f a -> {VV : () | Category.Functor.LH.fmap Category.Functor.LH.id x == Category.Functor.LH.id x}
    Sort Error in Refinement: {VV : Tuple | Category.Functor.LH.fmap Category.Functor.LH.id x == Category.Functor.LH.id x}
    Unbound symbol Category.Functor.LH.fmap --- perhaps you meant: Category.Functor.LH.C:Functor ?
    •
   |
45 |     fmap_id :: forall f a . Functor f => x : f a ->
   |

What am I doing wrong? My goal is to formulate this point-free with extensionality, but at least this point-wise formulation should work first.

Configuration:

Upvotes: 0

Views: 139

Answers (1)

jp.rider63
jp.rider63

Reputation: 584

Support for typeclasses is not yet supported in the official release of Liquid Haskell (although it is almost ready to merge). For now, you can use this fork which implements typeclass support. After recursively cloning the repository, you can install the forked version with:

pushd liquid-benchmark/liquidhaskell
stack install
popd

We define Functor and its laws (in the VFunctor subclass) in liquid-base/liquid-base/src/Data/Functor/Classes.hs as follows. Notice that you can specify refinements on the typeclass methods directly.

class Functor f where
  {-@ fmap :: forall a b. (a -> b) -> f a -> f b @-}
  fmap :: (a -> b) -> f a -> f b
  (<$) :: a -> f b -> f a

class Functor m => VFunctor m where
  {-@ lawFunctorId :: forall a . x:m a -> {fmap id x == id x} @-}
    lawFunctorId :: m a -> ()

  {-@ lawFunctorComposition :: forall a b c . f:(b -> c) -> g:(a -> b) -> x:m a -> { fmap (compose f g) x == compose (fmap f) (fmap g) x } @-}
  lawFunctorComposition :: forall a b c. (b -> c) -> (a -> b) -> m a -> ()

You can run the forked version of Liquid Haskell on the typeclass definitions with:

liquid --typeclass -i liquid-base/liquid-base/src/ liquid-base/liquid-base/src/Data/Functor/Classes.hs

We create a verified Maybe instance in liquid-base/liquid-base/src/Data/Maybe/Functor.hs with:

instance Functor Maybe where
  fmap _ Nothing = Nothing
  fmap f (Just x) = Just (f x)
  _ <$ Nothing = Nothing
  x <$ (Just _) = Just x

instance VFunctor Maybe where
  lawFunctorId Nothing = ()
  lawFunctorId (Just _) = ()
  lawFunctorComposition f g Nothing = ()
  lawFunctorComposition f g (Just x) = ()

You can run Liquid Haskell on the Maybe instance to verify that it satisfies the required laws:

liquid --typeclass -i liquid-base/liquid-base/src/ liquid-base/liquid-base/src/Data/Maybe/Functor.hs

Upvotes: 2

Related Questions