Generics isomorphisms - Haskell

Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.

Bolt

Hey all, can anyone help me figuring out how to do something with Generics?

Basically I have:

class Construct (Norm a) => ConstructNorm a where
    type Norm a
    toNorm :: a -> Norm a
    fromNorm :: Norm a -> a

instance ConstructNorm () where
    type Norm () = ()
    toNorm = Prelude.id
    fromNorm = Prelude.id

instance (ConstructNorm a, ConstructNorm b) => ConstructNorm (Either a b) where
    type Norm (Either a b) = Either (Norm a) (Norm b)
    toNorm = either (Left . toNorm) (Right . toNorm)
    fromNorm = either (Left . fromNorm) (Right . fromNorm)

-- Instance for other data types can be obtained mechanically using Generics
instance ConstructNorm Bool where
    type Norm Bool = Either () ()
    toNorm = bool (Left ()) (Right ())
    fromNorm = either (const False) (const True)

And I want to have an instance for arbitrary Generic types.

I already have type families that to this at the type level:

type family Count (d :: Type) :: Nat where
  Count (Natural n m) = (m - n) + 1
  Count (List a)      = (^) 2 (Count a)
  Count (Either a b)  = (+) (Count a) (Count b)
  Count (a, b)        = (*) (Count a) (Count b)
  Count (a -> b)      = (^) (Count b) (Count a)
  -- Generics
  Count (M1 _ _ f p)  = Count (f p)
  Count (K1 _ _ _)    = 1
  Count (V1 _)        = 0
  Count (U1 _)        = 1
  Count ((:*:) a b p) = Count (a p) * Count (b p)
  Count ((:+:) a b p) = Count (a p) + Count (b p)
  Count d             = Count (Rep d R)

type family FromNat (n :: Nat) :: Type where
  FromNat 0 = Void
  FromNat 1 = ()
  FromNat n = FromNat' (Mod n 2 == 0) (FromNat (Div n 2))

type family FromNat' (b :: Bool) (m :: Type) :: Type where
  FromNat' 'True m  = Either m m
  FromNat' 'False m = Either () (Either m m)

type family Normalize (d :: Type) :: Type where
  Normalize (Either a b) = Either (Normalize a) (Normalize b)
  Normalize d            = FromNat (Count d)

Is there any easy way to this at the term level?

TheMatten

So for every type, you want it's cardinality and "canonical representation" using Either and (,)?

Bolt

For every type I want an isomorphism with respect to its cardinality in a "canonical representation" using only Either

TheMatten

What would be the representation of Int?

Bolt

I have this so far:

instance ConstructNorm Bool where
    type Norm Bool = Either () ()
    toNorm = bool (Left ()) (Right ())
    fromNorm = either (const False) (const True)

instance ConstructNorm (U1 p) where
  type Norm (U1 p) = ()
  toNorm U1 = ()
  fromNorm _ = U1

instance (ConstructNorm (a p), ConstructNorm (b p)) => ConstructNorm ((:+:) a b p) where
  type Norm ((:+:) a b p) = Either (Norm (a p)) (Norm (b p))
  toNorm (L1 a) = Left (toNorm a)
  toNorm (R1 b) = Right (toNorm b)
  fromNorm (Left a) = L1 (fromNorm a)
  fromNorm (Right b) = R1 (fromNorm b)
Bolt

TheMatten said:

What would be the representation of Int?

Do not bother with this edge case

Bolt

But if you want it really bad then it should be equivalent to FromNat 9223372036854775807 I guess

TheMatten

Okay, so data A = B | C | D | .. goes to Either () (Either () (Either () ..))?
What about fields?

Bolt

I dont think a type can be Bounded if it has fields

Bolt

Or at least GHC cannot derive an instance to it, so it's off the record

TheMatten

Ah, I missed Bounded requirement - okay, then you simply want to match V1, U1, :+: and M1I guess?

TheMatten

Keep in mind though that Rep tries to keep branches similarly long and thus doesn't have nice, one-side-biased shape

Bolt

I want :*: too :frown: that's one of the hard parts..

TheMatten

But :*: only appears with fields, doesn't it?

TheMatten

Ah, you want tuples then?

TheMatten

Tuple is just constructor with fields

Bolt

But I want to transform a tuple into Eithers!

TheMatten

How do you do that, even in theory? You need some notion of a product - unless you go n * a = a + a + ... + a + a (n times)

Bolt

Yes that's the idea. If you see my type families what I do is I count the cardinality of the type with Count and then use FromNat to generate the Either type

Bolt

I think I did it!

toNorm :: (Enum a, Enum (Normalize a)) => a -> Normalize a
toNorm = toEnum . fromEnum
fromNorm :: (Enum a, Enum (Normalize a)) =>Normalize a -> a
fromNorm = toEnum . fromEnum

Since Count a ~ Count (Normalize a), under this restrictions this isomorphism is correct!

Sandy Maguire

you already have these functions; they're just to and from

Bolt

Hey @Sandy Maguire, I tried those but they did not quite fit my needs :confused: they work from/to a Generic Representation only. I couldn't have, for example f :: Ordering -> Either () (Either () ()) / g :: Either () (Either () ()) -> Ordering