Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing NFData instances #136

Open
goldfirere opened this issue May 1, 2022 · 6 comments
Open

Missing NFData instances #136

goldfirere opened this issue May 1, 2022 · 6 comments

Comments

@goldfirere
Copy link
Contributor

I was surprised not to find an NFData instance for Data.Parameterized.List.List. Are there possibly other NFData instances also missing?

@RyanGlScott
Copy link
Contributor

What instance in particular do you propose be added for List? I ask since I can think of multiple ways to implement such an instance, depending on what tradeoffs you're willing to make.

@goldfirere
Copy link
Contributor Author

OK. So this was a bit more involved than I originally thought, but not really too bad. Here is what I came up with:

type family AllC c f xs where
  AllC c f '[] = (() :: Constraint)
  AllC c f (x : xs) = (c (f x), AllC c f xs)

instance (AllC NFData f sh) => NFData (List f sh) where
  rnf Nil = ()
  rnf (x :< xs) = rnf x `seq` rnf xs

The auxiliary type family is annoying, but it seems generally useful in the context of this library anyway. An alternative is

instance (forall s. NFData (f s)) => NFData (List f sh) where ...

but that seems like it won't work for many structures, which would require some knowledge about s. So you could do

instance (forall s. NFData s => NFData (f s), All NFData sh) => NFData (List f sh) where ...

(for a suitable All that is like my AllC but without the f) but that restricts s to have kind Type. So, really, my first instance seems the most powerful and most likely to be useful.

Maybe you could argue that clients should just write their own instance? But that's unsatisfying, because the instance would likely be an orphan.

Or maybe the solution is to provide a rnfList function (or several) that allows the user to force a List without using NFData.

I think if this were my library, I would write the first instance (exporting a suitably documented AllC, perhaps from another module), but then have some documentation describing how other formats might suit particular use cases, inviting clients to write their own forcing function if necessary.

@RyanGlScott
Copy link
Contributor

Right, those were the three options that I had in mind as well. The AllC approach is the same approach that the generics-sop library uses when defining an NFData instance for its NP data type, where NP is isomorphic to List.

The reason why I'm hesitant to endorse that approach is that while it is in some sense the most powerful option, it doesn't quite fit the conventions used elsewhere in the library. For comparison, it's worth looking at the other instances for List, such as its Show instance:

instance ShowF f => Show (List f sh) where

Where ShowF f is effectively a synonym for forall tp. Show (f tp). You're right in observing that this approach rules out the ability to Show some structures, since the it won't work if tp itself requires a Show instance. In the context of parameterized-utils, however, that's usually an acceptable tradeoff. This is because all instantiations of f tp are type-indexed GADTs where the tp type parameter is only used for indexing. For example, this sort of instance works fine if you store structures like NatRepr or SymbolRepr.

From that perspective, my favorite of the three options is the instance (forall s. NFData (f s)) => NFData (List f sh) approach, perhaps with an accompanying NFDataF f = forall s. NFData (f s) synonym. I can understand the appeal behind the AllC approach, but I wonder if folks who want that would be better off using generics-sop instead.

@goldfirere
Copy link
Contributor Author

That sounds reasonable to me, too. (For the record, the instance you propose fits my use case.) That said, I found Data.Parameterized.List.List before the NP type and may end up switching to NP... but I don't really need generic sums-of-products.

@RyanGlScott
Copy link
Contributor

That sounds reasonable to me, too. (For the record, the instance you propose fits my use case.)

Great!

In that case, we should think carefully about whether we want to actually use quantified constraints to define NFDataF or not. There's nothing stopping us from doing so, although it would require parameterized-utils to raise its GHC support window a bit.

That said, I found Data.Parameterized.List.List before the NP type and may end up switching to NP... but I don't really need generic sums-of-products.

For what it's worth, the sop-core library exposes just the data types (NP, NS, etc.) without all of the additional generic programming–related bits.

@goldfirere
Copy link
Contributor Author

I missed sop-core -- thanks. In truth, that probably better serves my needs more than parameterized-utils (especially with the latter's lens dependency), but I do think the NFData instance we've worked out here would be generally beneficial.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants