Data families vs Injective type families

前端 未结 3 712
南旧
南旧 2020-12-13 15:00

Now that we have injective type families, is there any remaining use case for using data families over type families?

Looking at past StackOverflow questions about d

3条回答
  •  萌比男神i
    2020-12-13 15:29

    You're missing one other detail - data families create new types. Type families can only refer to other types. In particular, every instance of a data family declares new constructors. And it's nicely generic. You can create a data instance with newtype instance if you want newtype semantics. Your instance can be a record. It can have multiple constructors. It can even be a GADT if you want.

    It's exactly the difference between the type and data/newtype keywords. Injective type families don't give you new types, rendering them useless in the case where you need that.

    I understand where you're coming from. I had this same issue with the difference initially. Then I finally ran into a use case where they're useful, even without a type class getting involved.

    I wanted to write an api for dealing with mutable cells in a few different contexts, without using classes. I knew I wanted to do it with a free monad with interpreters in IO, ST, and maybe some horrible hacks with unsafeCoerce to even go so far as shoehorning it into State. This wasn't for any practical purpose, of course - I was just exploring API designs.

    So I had something like this:

    data MutableEnv (s :: k) a ...
    
    newRef :: a -> MutableEnv s (Ref s a)
    readRef :: Ref s a -> MutableEnv s a
    writeRef :: Ref s a -> a -> MutableEnv s ()
    

    The definition of MutableEnv wasn't important. Just standard free/operational monad stuff with constructors matching the three functions in the api.

    But I was stuck on what to define Ref as. I didn't want some sort of class, I wanted it to be a concrete type as far as the type system was concerned.

    Then late one night I was out for a walk and it hit me - what I essentially want is a type whose constructors are indexed by an argument type. But it had to be open, unlike a GADT - new interpreters could be added at will. And then it hit me. That's exactly what a data family is. An open, type-indexed family of data values. I could complete the api with just the following:

    data family Ref (s :: k) :: * -> *
    

    Then, dealing with the underlying representation for a Ref was no big deal. Just create a data instance (or newtype instance, more likely) whenever an interpreter for MutableEnv is defined.

    This exact example isn't really useful. But it clearly illustrates something data families can do that injective type families can't.

提交回复
热议问题