[Haskell-cafe] How to deal with type family applications in type equalities?

Jakob Brünker jakob.bruenker at gmail.com
Thu Feb 18 04:52:19 UTC 2021


Imagine you want to have a type level function that calculates the
element-wise OR on two bitstrings, encoded as length-indexed vectors filled
with Bools (this is a simplification of something I need for a project).
The vectors should be "aligned to the right" as it were, such that the new
right-most value is True if the right-most value of the first vector OR the
right-most value of the second vector was True.

Example (replacing True and False with 1 and 0 for readability):
ElementwiseOr [1,0,1,1] [0,1,1,0,0,0,0,1]
= [0,1,1,0,1,0,1,1]

Its type would be something like
ElementwiseOr :: Vec n Bool -> Vec m Bool -> Vec (Max n m) Bool

I have written the following code:

---------------------------------------------------------------------------
{-# LANGUAGE TypeFamilies, GADTs, DataKinds, StandaloneKindSignatures,
             PolyKinds, TypeOperators, RankNTypes, TypeApplications,
             UndecidableInstances #-}

import Data.Kind
import Data.Type.Equality

data Nat = Z | S Nat

infixr 5 :<

type Vec :: Nat -> Type -> Type
data Vec n a where
  Nil :: Vec Z a
  (:<) :: a -> Vec n a -> Vec (S n) a

type Cong :: forall f -> a :~: b -> f a :~: f b
type family Cong f p where
  Cong f Refl = Refl

type Max :: Nat -> Nat -> Nat
type family Max n m where
  Max Z m = m
  Max n Z = n
  Max (S n) (S m) = S (Max n m)

-- The reason for this slightly convoluted Ordering type is that it seems
-- to make the "right-alignment" easier.
type NatOrdering :: Ordering -> Nat -> Nat -> Type
data NatOrdering o n m where
  NOLTE :: NatOrdering EQ n m -> NatOrdering LT n (S m)
  NOLTS :: NatOrdering LT n m -> NatOrdering LT n (S m)
  NOEQZ :: NatOrdering EQ Z Z
  NOEQS :: NatOrdering EQ n m -> NatOrdering EQ (S n) (S m)
  NOGTE :: NatOrdering EQ n m -> NatOrdering GT (S n) m
  NOGTS :: NatOrdering GT n m -> NatOrdering GT (S n) m

type MaxSIsSMaxEQ
  :: forall n m . NatOrdering EQ n m -> Max n (S m) :~: S (Max n m)
type family MaxSIsSMaxEQ o where
  MaxSIsSMaxEQ NOEQZ = Refl
  MaxSIsSMaxEQ (NOEQS o) = Cong S (MaxSIsSMaxEQ o)

type TransportVec :: n :~: m -> Vec n a -> Vec m a
type family TransportVec p v where
  TransportVec Refl v = v

type ElementwiseOr
  :: NatOrdering o n m -> Vec n Bool -> Vec m Bool -> Vec (Max n m) Bool
type family ElementwiseOr o u v where
  ElementwiseOr (NOLTE o) u (s :< v) =
    TransportVec (MaxSIsSMaxEQ o) (s :< ElementwiseOr o u v) -- XXX
  -- other equations ommitted
---------------------------------------------------------------------------

To me, the equation at the end marked with XXX seems like it should work.
However, it produces the following error:

    • Couldn't match kind: Max n ('S m)
                     with: 'S (Max n m)
      Expected kind ‘'S (Max n m) :~: 'S (Max n m)’,
        but ‘MaxSIsSMaxEQ o’ has kind ‘Max n ('S m) :~: 'S (Max n m)’
    • In the first argument of ‘TransportVec’, namely
        ‘(MaxSIsSMaxEQ o)’

So it expects something of kind `S (Max n m) :~: S (Max n m)` - it seems
like Refl fits that bill, but that doesn't work either, because replacing
(MaxSIsSMaxEQ o) with Refl means that TransportVec returns a type of the
wrong kind, producing a very similar error.

I suspect the reason this doesn't work is because type equality isn't
equipped to properly handle type family applications. I could potentially
work around this if I defined Max as

data Max n m r where
  MaxZZ :: Max Z Z Z
  MaxSZ :: Max (S n) Z (S n)
  MaxZS :: Max Z (S m) (S m)
  MaxSS :: Max n m r -> Max (S n) (S m) (S r)

However, dealing with a proof object like this is a lot less convenient
than just being able to use a type family for most purposes, so I'd like to
avoid this if possible.

Is there a way to make this work while sticking with type families?

Thanks

Jakob
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20210218/cdaf7151/attachment.html>


More information about the Haskell-Cafe mailing list