Enumerating all values of a nested sum type?

Say there is a sum type that has a layer of nesting, like so:

data LandVehicle = Car | Truck
  deriving (Eq, Enum, Bounded)

data MarineVehicle = Submarine | Tugboat
  deriving (Eq, Enum, Bounded)

data Vehicle = Land LandVehicle | Marine MarineVehicle

Is there some generic version of enumerate that will produce a list containing all four possible values of Vehicle?

  • Land Car
  • Land Truck
  • Marine Submarine
  • Marine Tugboat

There’s a similar question on StackOverflow that makes reference to rolling one’s own implementation with Typeable/Data. But does there exist a package that provides this functionality out of the box?

Leancheck uses its own Listable class. It also has a TH function to derive instances automatically.

https://hackage.haskell.org/package/universe-1.2.3/docs/Data-Universe.html

This looks like what you’re searching for. It also has a universeGeneric function. Handrolling your own with generics-sop or even GHC.Generics is a good exercise and should be pretty easy to get through, though.

5 Likes

My go-to choice for this problem is

8 Likes

I normally reach for universe, and I haven’t heard of finitary before. Is there a compelling reason to switch?

EDIT: Oh, cool, finitary gets you a type-level cardinality for the type!

1 Like

finitary provides 1-to-1 function between a and Finite (Cardinality a), which is very powerful. Basically class Enum done right.


There is also

Similarly to finitary but without fancy Finite, it provides a -> Integer and Integer -> a and offers instances to enumerate functions.

2 Likes

There is also generic-enumeration if you want a generics-based approach.

2 Likes

Very cool! I handrolled an implementation with TH, but I wish I’d known about these packages! Do any of these packages work with GADTs? e.g. I’d like something like:

data Type a where
  Int :: Type Int
  Bool :: Type Bool
  Text :: Type Text

-- Some from some package
everything = [Some Int, Some Bool, Some Text]

generic-enumerations probably should in theory to the extent you can get a Generic instance for a GADT in the first place, which I understand is quite difficult. I’ve never tried to make a Generic instance for a GADT, but I found this article on the matter: How to derive Generic for (some) GADTs using QuantifiedConstraints - Ryan Scott

Finitary is lovely, to the extent I wish it was in base - "class Enum done right" is a very good description. Just worth noting the GPLv3 license can be a bit scary.

You can also derive the Enum instance for Vehicle by wrapping it with the FiniteEnumeration newtype from the generic-data package:

data LandVehicle = Car | Truck
  deriving (Eq, Enum, Bounded)

data MarineVehicle = Submarine | Tugboat
  deriving (Eq, Enum, Bounded)

data Vehicle = Land LandVehicle | Marine MarineVehicle
  deriving Generic
  deriving (Enum, Bounded) via (FiniteEnumeration Vehicle)

There’s also this comment on the GHC issue tracker, which explains the situation around supporting generics for GADTs, three years ago.