# HKD: best or worst thing ever?

**URL:** <https://discourse.haskell.org/t/hkd-best-or-worst-thing-ever/9450>\
**Category:** Learn\
**Created:** [May 1, 2024, 1:42pm UTC](https://discourse.haskell.org/t/hkd-best-or-worst-thing-ever/9450 "2024-05-01T13:42:39Z")\
**Posts on this page:** 1\
**Showing post:** 7

<div class="post-metadata">

**Author:** ![rhendric](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/rhendric/32/2689_2.png) [@rhendric](https://discourse.haskell.org/u/rhendric)\
**Post date:** [May 1, 2024, 10:31pm UTC](https://discourse.haskell.org/t/hkd-best-or-worst-thing-ever/9450/7 "2024-05-01T22:31:29Z")

</div>

> [@lucasdicioccio](#):
>
> I found the boilerplate of “writing my own HKD” pretty minimal versus other approaches (having one data per occurrence with the certainty of hitting duplicated-record-fields, or one gigantic unreadable sum-type with a branch per occurrence).

Many uses of HKD in the wild are only ever used to toggle between `Maybe` and `Identity`, or `NonEmpty` and `Identity`—simple cardinality restrictions, in other words. In such cases, I tend to favor the following lower-kinded approach for its simplicity:

```haskell
-- For toggling between 0-or-1 and exactly-1:
data Foo opt = Foo {
  field1 :: Either opt Int,
  field2 :: Either opt String,
  field3 :: Either opt Bool
}
type FooOptionally = Foo ()
type FooDefinitely = forall a. Foo a

-- For toggling between 1-or-more and exactly-1:
data MaybeMore more a = MaybeMore a (Maybe (more, NonEmpty a))
  deriving Functor
data Foo more = Foo {
  field1 :: MaybeMore more Int,
  field2 :: MaybeMore more String,
  field3 :: MaybeMore more Bool
}
type FooPlural = Foo ()
type FooSingular = forall a. Foo a

-- Some helper patterns I usually use for matching on these things:

pattern CompletelyRight :: a -> Either Void a
pattern CompletelyRight a <- (either absurd id -> a)
  where CompletelyRight a = Right a
{-# COMPLETE CompletelyRight #-}

pattern It'sNothing :: Maybe (Void, a)
pattern It'sNothing <- (maybe () (absurd . fst) -> ())
  where It'sNothing = Nothing
{-# COMPLETE It'sNothing #-}

```

This avoids both giant sum types and representable invalid states, sometimes at the cost of a little extra boxing relative to HKD. It’s pretty manageable.

And how could I forget to mention the best part: the exactly-1 case is a true subtype of the others, as it intuitively ought to be! No conversion, not even a `coerce`, is necessary to use a `FooDefinitely` value where `FooOptionally` is desired. Try that with HKD.

---

_[View the full topic](https://discourse.haskell.org/t/hkd-best-or-worst-thing-ever/9450)._
