How do I map over parameters? - haskell

Given the type X = X Int Int, I want to define a function toX :: [String] -> X which constructs an X during runtime with generics.
This is easy when I just write it down like this:
toX :: [String] -> X
toX (x:[y]) = to (M1 (M1 (M1 (K1 $ read x) :*: (M1 (K1 $ read y)))))
But I don't know how to do it recursive (in case we have a lot more than two parameters). My first try was something like this:
toX xs = to (M1 (M1 (toX' xs)))
toX' (x:[]) = M1 (K1 x)
toX' (x:xs) = M1 (K1 x) :*: (toX' xs)
which (of course) fails with a type error. Looking at the type of (:*:) confuses me even more: (:*:) :: f p -> g p -> (:*:) f g p. I have absolutely no idea what this type is supposed to mean and how to proceed from here.
Any hints?
#!/usr/bin/env stack
{- stack --resolver lts-8.4 runghc-}
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
data X = X Int Int deriving (Generic, Show)
main :: IO ()
main = do
print $ toXeasy ["2","4"]
-- print $ toX ["2","4"]
toXeasy :: [String] -> X
toXeasy (x:[y]) = to (M1 (M1 (M1 (K1 $ read x) :*: (M1 (K1 $ read y)))))
--toX :: [String] -> X
--toX xs = to (M1 (M1 (toX' xs)))
--toX' (x:[]) = M1 (K1 x)
--toX' (x:xs) = M1 (K1 x) :*: (toX' xs)

This defines a function readFields :: [String] -> Maybe X for any Generic data type X which has only one constructor (with at least one field).
readFields is defined using a generic version gReadFields which works with generic representations (i.e., types constructed using type constructors that appear in GHC.Generics: M1, (:*:), K1...).
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators #-}
module A where
import GHC.Generics
import Control.Monad.Trans.State
import Text.Read
data X = X Int Int deriving (Generic, Show)
main = print (readFields ["14", "41"] :: Maybe X)
readFields :: (Generic a, GReadableFields (Rep a)) => [String] -> Maybe a
readFields xs = fmap to (evalStateT gReadFields xs)
class GReadableFields f where
gReadFields :: StateT [String] Maybe (f p)
instance GReadableFields f => GReadableFields (M1 i c f) where
gReadFields = fmap M1 gReadFields
-- When your type is a large product, you cannot assume that
-- the generic product structure formed using `(:*:)` is list-
-- like (field1 :*: (field2 :*: (field3 ...)), so it is not
-- clear how to split the input list of strings to read each
-- component. For that reason we use `State`. Another possible way
-- is to compute the number of fields of the two operands `f` and `g`.
instance (GReadableFields f, GReadableFields g) => GReadableFields (f :*: g) where
gReadFields = do
f <- gReadFields
g <- gReadFields
return (f :*: g)
instance Read c => GReadableFields (K1 i c) where
gReadFields = StateT $ \(x : xs) -> do
c <- readMaybe x
return (K1 c, xs)
Just for fun, here is a way of achieving a similar results which does not use generics. The user has to provide a constructor (or a function), and a type class takes care of filling all its arguments with values read from the list of strings.
{-# LANGUAGE FlexibleInstances, TypeFamilies #-}
module A where
data X = X Int Int deriving Show
main = print (readFields X ["14", "41"])
type family Result a where
Result (a -> b) = Result b
Result a = a
class ReadableFields a where
readFields :: a -> [String] -> Maybe (Result a)
instance {-# OVERLAPPING #-} (ReadableFields b, Read a) => ReadableFields (a -> b) where
readFields f (x : xs) = do
a <- readMaybe x
readFields (f a) xs
readFields _ _ = Nothing
instance (Result a ~ a) => ReadableFields a where
readFields a _ = Just a
EDIT
That use of Generic is straightforward enough that the underlying pattern is packaged in one-liner.
{-# LANGUAGE FlexibleContexts #-}
import Generics.OneLiner
import Control.Monad.Trans.State
import Text.Read
Define an action to read a single field. It is important that there is an instance Applicative (StateT [String] Maybe) so that it can be composed.
-- Takes a string from the state and reads it out.
readM :: Read a => StateT [String] Maybe a
readM = StateT readM'
where
readM' (x : xs) | Just a <- readMaybe x = Just (a, xs)
readM' _ = Nothing
This is now a one-liner, using createA from the one-liner library.
readFields xs = evalStateT (createA (For :: For Read) readM) xs
main = print (readFields ["14", "42"] :: Maybe (Int, Int))

Here is a solution using generics-sop:
{-# LANGUAGE DataKinds, TypeFamilies, FlexibleContexts, TypeApplications #-}
{-# LANGUAGE TemplateHaskell #-}
module ReadFields where
import Data.Maybe
import Generics.SOP
import Generics.SOP.TH
readFields ::
(Generic a, Code a ~ '[ xs ], All Read xs) => [String] -> Maybe a
readFields xs =
to . SOP . Z . hcmap (Proxy #Read) (I . read . unK) <$> fromList xs
data X = X Int Int
deriving Show
deriveGeneric ''X
Testing:
GHCi> readFields #X ["3", "4"]
Just (X 3 4)
GHCi> readFields #X ["3"]
Nothing

Related

QuickCheck returns "0 tests" when checking Applicative homomorphism property (Binary Tree)

I would like to check that homomorphism Applicative law holds for datatype BinTree:
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
module Laws where
import Control.Applicative ((<$>), liftA3)
import Data.Monoid
import Test.QuickCheck
import Test.QuickCheck.Function
import Test.QuickCheck.Gen
data BinTree a = Empty | Node a (BinTree a) (BinTree a) deriving (Show, Eq)
instance Functor BinTree where
fmap _ Empty = Empty
fmap f (Node x hi hd) = Node (f x) (fmap f hi) (fmap f hd)
instance Applicative BinTree where
-- pure :: a -> BinTree a
pure x = Node x (pure x) (pure x)
-- <*> :: BinTree (a -> b) -> BinTree a -> BinTree b
_ <*> Empty = Empty -- L1,
Empty <*> t = Empty
(Node f l r) <*> (Node x l' r') = Node (f x) (l <*> l') (r <*> r')
instance (Arbitrary a) => Arbitrary (BinTree a) where
arbitrary = oneof [return Empty, -- oneof :: [Gen a] -> Gen a
liftA3 Node arbitrary arbitrary arbitrary]
-- Identity
apIdentityProp :: (Applicative f, Eq (f a)) => f a -> Bool
apIdentityProp v = (pure id <*> v) == v
apHomomorphismProp :: forall f a b. (Applicative f, Eq (f b)) => Fun a b -> a -> Bool
apHomomorphismProp (apply -> g) x = (pure #f g <*> pure x) == pure (g x)
main = quickCheck $ apHomomorphismProp #BinTree #Int #Int
However, when I execute the code, quickCheck applied to the applicative property returns:
(0 tests)
How can I solve this issue ?
Quite simply, your pure implementation generates an infinite tree. <*> preserves the infinite size of the trees on both of its sides. Then you compare the resulting infinite tree for equality with another infinite tree.
Well, it evidently doesn't find any discrepancy between them... but it doesn't terminate either. So QuickCheck never actually manages to confirm even one test case correct.
One way out could be to use not == but an equality operator which only checks for equality down to a limited depth, and assumes it'll be equal further down as well. (Note that it will still be exponentially expensive, so you can't even go to very great depth!)

Quickcheck Applicative homomorphism law for Binary Tree

I am aware that the following question exists:
haskell - How to quickcheck applicative homomorphism property? - Stack Overflow
However, the introduction of the following PRAGMA
{-# LANGUAGE ScopedTypeVariables #-}
didn't solve my issue.
These are my definitions:
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Laws where
import Control.Applicative ((<$>), liftA3)
import Data.Monoid
import Test.QuickCheck
import Test.QuickCheck.Function
import Test.QuickCheck.Gen
data BinTree a = Empty | Node a (BinTree a) (BinTree a) deriving (Show, Eq)
instance Functor BinTree where
fmap _ Empty = Empty
fmap f (Node x hi hd) = Node (f x) (fmap f hi) (fmap f hd)
instance Applicative BinTree where
-- pure :: a -> BinTree a
pure x = Node x (pure x) (pure x)
-- <*> :: BinTree (a -> b) -> BinTree a -> BinTree b
_ <*> Empty = Empty -- L1,
Empty <*> t = Empty
(Node f l r) <*> (Node x l' r') = Node (f x) (l <*> l') (r <*> r')
instance (Arbitrary a) => Arbitrary (BinTree a) where
arbitrary = oneof [return Empty, -- oneof :: [Gen a] -> Gen a
liftA3 Node arbitrary arbitrary arbitrary]
-- Identity
apIdentityProp :: (Applicative f, Eq (f a)) => f a -> Bool
apIdentityProp v = (pure id <*> v) == v
-- pure f <*> pure x = pure (f x) -- Homomorphism
apHomomorphismProp :: forall f a b. (Applicative f, Eq (f b)) => Fun a b -> a -> Bool
apHomomorphismProp (apply -> g) x = (((pure g :: f (a -> b)) <*> (pure x :: f a)) :: f b) == (pure (g x) :: f b)
main :: IO ()
main = quickCheck (apHomomorphismProp :: Fun Int Int -> Int -> Bool)
How can I fix the following error ?
Could not deduce (Applicative f0)
from the context: (Applicative f, Eq (f b))
It would have been easier to analyse the problem if you had included the full error message, which mentions an ambiguous type variable. The thing that GHC is complaining about is that f does not appear anywhere in the type signature of apHomomorphismProp, except in the quantifier and constraints.
Why is that a problem? Well, it isn't a problem... but it used to be in older Haskell versions, because there was no way for the compiler to tell when you're using apHomomorphismProp what applicative it's supposed to test here. In fact this is still the case with the way you are using it: apHomomorphismProp :: Fun Int Int -> Int -> Bool does not mention BinTree in any way, so how is the compiler supposed to know that's what you mean? For all it knows, you could as well be asking for, say, the Maybe applicative to be tested here.
The solution, in modern Haskell, is -XTypeApplications, which just lets you explicitly say what a type variable should be instantiated with.
{-# LANGUAGE TypeApplications #-}
main = quickCheck (apHomomorphismProp #BinTree :: Fun Int Int -> Int -> Bool)
In fact I would recommend also using this syntax to clarify the Int types:
main = quickCheck $ apHomomorphismProp #BinTree #Int #Int
However, there was still the compiler error with apHomomorphismProp, which is all because prior to TypeApplications, a signature like the one you gave to apHomomorphismProp was useless. But this restriction is now obsolete†, and it can be disabled with -XAllowAmbiguousTypes:
{-# LANGUAGE ScopedTypeVariables, UnicodeSyntax, AllowAmbiguousTypes, TypeApplications #-}
apHomomorphismProp :: ∀ f a b. (Applicative f, Eq (f b)) => Fun a b -> a -> Bool
apHomomorphismProp (apply -> g) x = (pure #f g <*> pure x) == pure (g x)
Note that I only need to mention #f for one of the pures, the other ones are automatically constrained to the same applicative.
†It's arguable whether it's really obsolete. What's probably still true is that if a beginner gives their function an ambiguous type, it's more likely a mistake that should be caught right there and then, rather than something that's actually intended for use with -XTypeApplications. An unintentionally ambiguous type can cause quite confusing errors further down the line.

Automatically generate mapping function for data with higher-kinded parameter

Consider the data type
data Foo f = Foo {fooInt :: f Int, fooBool :: f Bool}
I would like a function mapFoo :: (forall a. f a -> g a) -> Foo f -> Foo g. My options:
I could write it manually. This is mildly annoying, but the killer objection is that I expect Foo to gain fields over time and I want that to be as frictionless as possible, so having to add a case to this function is annoying.
I could write Template Haskell. I'm pretty sure this isn't too hard, but I tend to view TH as a last resort, so I'm hoping for something better.
Could I use generics? I derived Generic, but when I tried to implement the K1 case (specifically to handle Rec0) I couldn't figure out how to do it; I needed it to change the type.
Is there a fourth option that I just missed?
If there is a generic way to write mapFoo without reaching for Template Haskell, I'd love to know about it! Thanks.
The rank2classes package can derive this for you.
{-# LANGUAGE TemplateHaskell #-}
import Rank2.TH (deriveFunctor)
data Foo f = Foo {fooInt :: f Int, fooBool :: f Bool}
$(deriveFunctor ''Foo)
Now you can write mapFoo = Rank2.(<$>).
EDIT: Oh, I should be explicit that this is a manual method - it's a pointer to a package that has lots of useful functions and type classes but afaik no TH to generate what you want. Pull requests welcome, I'm sure.
The parameterized-utils package provides a rich set of higher rank classes. For your needs there's FunctorF:
-- | A parameterized type that is a function on all instances.
class FunctorF m where
fmapF :: (forall x . f x -> g x) -> m f -> m g
And the instances are what you probably expect:
{-# LANGUAGE RankNTypes #-}
import Data.Parameterized.TraversableF
data Foo f = Foo {fooInt :: f Int, fooBool :: f Bool}
instance FunctorF Foo where
fmapF op (Foo a b) = Foo (op a) (op b)
Here is GHC.Generics-based implementation if you still prefer not to use TemplateHaskell:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
import GHC.Generics
data Foo f = Foo {
fooInt :: f Int,
fooBool :: f Bool,
fooString :: f String
} deriving (Generic)
class Functor2 p q f where
fmap2 :: (forall a. p a -> q a) -> f p -> f q
instance (Generic (f p), Generic (f q), GFunctor2 p q (Rep (f p)) (Rep (f q))) => Functor2 p q f where
fmap2 f = to . (gfmap2 f) . from
class GFunctor2 p q f g where
gfmap2 :: (forall a. p a -> q a) -> f x -> g x
instance (GFunctor2 p q a b) => GFunctor2 p q (D1 m1 (C1 m2 a)) (D1 m1 (C1 m2 b)) where
gfmap2 f (M1 (M1 a)) = M1 (M1 (gfmap2 f a))
instance (GFunctor2 p q a c, GFunctor2 p q b d) => GFunctor2 p q (a :*: b) (c :*: d) where
gfmap2 f (a :*: b) = gfmap2 f a :*: gfmap2 f b
instance GFunctor2 p q (S1 m1 (Rec0 (p a))) (S1 m1 (Rec0 (q a))) where
gfmap2 f (M1 (K1 g)) = M1 (K1 (f g))
-- Tests
foo = Foo (Just 1) (Just True) (Just "foo")
test1 = fmap2 (\(Just a) -> [a]) foo
test2 = fmap2 (\[a] -> Left "Oops") test1
I'm not sure though if it is possible to avoid MultiParamTypeClasses to make class Functor2 identical to the one defined rank2classes.

Defining arity-generic lift

I'm trying to define liftN for Haskell. The value-level implementation in dynamically typed languages like JS is fairly straightforward, I'm just having trouble expressing it in Haskell.
After some trial and error, I arrived at the following, which typechecks (note the entire implementation of liftN is undefined):
{-# LANGUAGE FlexibleContexts, ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}
import Data.Proxy
import GHC.TypeLits
type family Fn x (y :: [*]) where
Fn x '[] = x
Fn x (y:ys) = x -> Fn y ys
type family Map (f :: * -> *) (x :: [*]) where
Map f '[] = '[]
Map f (x:xs) = (f x):(Map f xs)
type family LiftN (f :: * -> *) (x :: [*]) where
LiftN f (x:xs) = (Fn x xs) -> (Fn (f x) (Map f xs))
liftN :: Proxy x -> LiftN f x
liftN = undefined
This gives me the desired behavior in ghci:
*Main> :t liftN (Proxy :: Proxy '[a])
liftN (Proxy :: Proxy '[a]) :: a -> f a
*Main> :t liftN (Proxy :: Proxy '[a, b])
liftN (Proxy :: Proxy '[a, b]) :: (a -> b) -> f a -> f b
and so on.
The part I'm stumped on is how to actually implement it. I was figuring maybe the easiest way is to exchange the type level list for a type level number representing its length, use natVal to get the corresponding value level number, and then dispatch 1 to pure, 2 to map and n to (finally), the actual recursive implementation of liftN.
Unfortunately I can't even get the pure and map cases to typecheck. Here's what I added (note go is still undefined):
type family Length (x :: [*]) where
Length '[] = 0
Length (x:xs) = 1 + (Length xs)
liftN :: (KnownNat (Length x)) => Proxy x -> LiftN f x
liftN (Proxy :: Proxy x) = go (natVal (Proxy :: Proxy (Length x))) where
go = undefined
So far so good. But then:
liftN :: (Applicative f, KnownNat (Length x)) => Proxy x -> LiftN f x
liftN (Proxy :: Proxy x) = go (natVal (Proxy :: Proxy (Length x))) where
go 1 = pure
go 2 = fmap
go n = undefined
...disaster strikes:
Prelude> :l liftn.hs
[1 of 1] Compiling Main ( liftn.hs, interpreted )
liftn.hs:22:28: error:
* Couldn't match expected type `LiftN f x'
with actual type `(a0 -> b0) -> (a0 -> a0) -> a0 -> b0'
The type variables `a0', `b0' are ambiguous
* In the expression: go (natVal (Proxy :: Proxy (Length x)))
In an equation for `liftN':
liftN (Proxy :: Proxy x)
= go (natVal (Proxy :: Proxy (Length x)))
where
go 1 = pure
go 2 = fmap
go n = undefined
* Relevant bindings include
liftN :: Proxy x -> LiftN f x (bound at liftn.hs:22:1)
|
22 | liftN (Proxy :: Proxy x) = go (natVal (Proxy :: Proxy (Length x))) where
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
At this point it isn't clear to me what exactly is ambiguous or how to disambiguate it.
Is there a way to elegantly (or if not-so-elegantly, in a way that the inelegance is constrained to the function implementation) implement the body of liftN here?
There are two issues here:
You need more than just the natVal of a type-level number to ensure the whole function type checks: you also need a proof that the structure you're recursing on corresponds to the type-level number you're referring to. Integer on its own loses all of the type-level information.
Conversely, you need more runtime information than just the type: in Haskell, types have no runtime representation, so passing in a Proxy a is the same as passing in (). You need to get in runtime info somewhere.
Both of these problems can be addressed using singletons, or with classes:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
data Nat = Z | S Nat
type family AppFunc f (n :: Nat) arrows where
AppFunc f Z a = f a
AppFunc f (S n) (a -> b) = f a -> AppFunc f n b
type family CountArgs f where
CountArgs (a -> b) = S (CountArgs b)
CountArgs result = Z
class (CountArgs a ~ n) => Applyable a n where
apply :: Applicative f => f a -> AppFunc f (CountArgs a) a
instance (CountArgs a ~ Z) => Applyable a Z where
apply = id
{-# INLINE apply #-}
instance Applyable b n => Applyable (a -> b) (S n) where
apply f x = apply (f <*> x)
{-# INLINE apply #-}
-- | >>> lift (\x y z -> x ++ y ++ z) (Just "a") (Just "b") (Just "c")
-- Just "abc"
lift :: (Applyable a n, Applicative f) => (b -> a) -> (f b -> AppFunc f n a)
lift f x = apply (fmap f x)
{-# INLINE lift #-}
This example is adapted from Richard Eisenberg's thesis.

Is it possible to re-implement `Enum` deriving using GHC generics

Is it possible to re-implement deriving of Enum type class using GHC
generics?
At first, it looks easy:
data Foo -- representation without metadata (wrong):
= Foo -- L1 U1
| Bar -- R1 (L1 U1)
| Baz -- R1 (R1 (L1 U1))
| Quux -- R1 (R1 (R1 U1))
deriving (Show, Eq, Generic)
-- Rep Foo p = (U1 :+: (U1 :+: (U1 :+: U1))) p
instance Enum Foo where
toEnum = undefined -- FIXME
fromEnum = gfromEnum . from
class GEnum f where
gfromEnum :: f p -> Int
instance GEnum U1 where
gfromEnum U1 = 0
instance GEnum f => GEnum (M1 i t f) where
gfromEnum (M1 x) = gfromEnum x
instance (GEnum x, GEnum y) => GEnum (x :+: y) where
gfromEnum (L1 x) = gfromEnum x
gfromEnum (R1 y) = 1 + gfromEnum y
However, this is not going to work:
λ> fromEnum Foo
0
λ> fromEnum Bar
1
λ> fromEnum Baz
1
λ> fromEnum Quux
2
This is because we can't rely on how arguments of (:+:) are grouped. In
this case it seems they are nested like this:
((U1 :+: U1) :+: (U1 :+: U1)) p
So, is it possible to derive Enum using Generics? If yes, how?
GHC derives Generic such that the L and R variants form a tree where the leaves are in Enum order. Consider the following example (with trimmed output):
ghci> data D = A | B | C | D | E deriving (Generic)
ghci> from A
L1 (L1 U1)
ghci> from B
L1 (R1 U1)
ghci> from C
R1 (L1 U1)
ghci> from D
R1 (R1 (L1 U1))
ghci> from E
R1 (R1 (R1 U1)))
Notice that if you arranged these as a tree, toEnum `map` [1..] is going to be the left to right traversal of the leaves. With that intuition, we'll start by defining a GLeaves class which counts the number of leaves that a generic type (not a value!) has in its tree.
{-# LANGUAGE ScopedTypeVariables, PolyKinds, TypeApplications, TypeOperators,
DefaultSignatures, FlexibleContexts, TypeFamilies #-}
import GHC.Generics
import Data.Proxy
class GLeaves f where
-- | Counts the number of "leaves" (i.e. U1's) in the type `f`
gSize :: Proxy f -> Int
instance GLeaves U1 where
gSize _ = 1
instance GLeaves x => GLeaves (M1 i t x) where
gSize _ = gSize (Proxy :: Proxy x)
instance (GLeaves x, GLeaves y) => GLeaves (x :+: y) where
gSize _ = gSize (Proxy :: Proxy x) + gSize (Proxy :: Proxy y)
Now, we are in shape to define GEnum. As is usual with this setup, we define our class Enum' and have default signatures that rely on GEnum.
class Enum' a where
toEnum' :: Int -> a
fromEnum' :: a -> Int
default toEnum' :: (Generic a, GEnum (Rep a)) => Int -> a
toEnum' = to . gToEnum
default fromEnum' :: (Generic a, GEnum (Rep a)) => a -> Int
fromEnum' = gFromEnum . from
class GEnum f where
gFromEnum :: f p -> Int
gToEnum :: Int -> f p
Finally, we get to the good stuff. For U1 and M1, gFromEnum and gToEnum are both straightforward. For :+:, gFromEnum needs to find all of the leaves to the left of it, so if it is the right subtree we add the size of the left subtree (and if it is the left subtree we add nothing). Similarly, gToEnum, checks whether it belong in the left or right subtree by checking if it is smaller than the number of leaves in the left subtree.
instance GEnum U1 where
gFromEnum U1 = 0
gToEnum n = if n == 0 then U1 else error "Outside enumeration range"
instance GEnum f => GEnum (M1 i t f) where
gFromEnum (M1 x) = gFromEnum x
gToEnum n = M1 (gToEnum n)
instance (GLeaves x, GEnum x, GEnum y) => GEnum (x :+: y) where
gFromEnum (L1 x) = gFromEnum x
gFromEnum (R1 y) = gSize (Proxy :: Proxy x) + gFromEnum y
gToEnum n = let s = gSize (Proxy :: Proxy x)
in if n < s then L1 (gToEnum n) else R1 (gToEnum (n - s))
Finally, you can test this in GHCi:
ghci> :set -XDeriveAnyClass -XDeriveGeneric
ghci> data D = A | B | C | D | E deriving (Show, Generic, Enum, Enum')
ghci> toEnum `map` [0 .. 4] :: [D]
[A,B,C,D,E]
ghci> toEnum' `map` [0 .. 4] :: [D]
[A,B,C,D,E]
ghci> fromEnum `map` [A .. E] :: [Int]
[A,B,C,D,E]
ghci> fromEnum' `map` [A .. E] :: [Int]
[A,B,C,D,E]
Performance
You may be thinking to yourself: this is super inefficient! We end up recalculating a bunch of sizes over and over - the worst case performance is at least O(n^2). The catch is that (hopefully), GHC will be able to optimize/inline the hell out of our specific Enum' instances until there is nothing left of the initial Generic structure.
Enum is one of many examples that are slightly awkward to write using the standard GHC Generics representation, because a lot of the structure of datatypes is left implicit (e.g. how sum and product constructors are nested, and where metadata occurs).
With generics-sop, you can (re-)define generic Enum instances in a slightly more straight-forward way:
{-# LANGUAGE ConstraintKinds, DataKinds, DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts, GADTs, PolyKinds #-}
{-# LANGUAGE TypeApplications, TypeOperators #-}
import Generics.SOP
import qualified GHC.Generics as GHC
We define a type synonym that captures what it means to be an enumeration type:
type IsEnumType a = All ((~) '[]) (Code a)
(Unfortunately, the (~) '[] construction triggers a bug in GHC 8.0.1, but it works fine in GHC 8.0.2.) In generics-sop, the code of a datatype is a type-level list of lists. The outer list contains an element for each constructor, the inner lists contain the types of the constructor arguments. The IsEnumType constraint says that all of the inner lists have to be empty, which means that none of the constructors must have any arguments.
gfromEnum :: (Generic a, IsEnumType a) => a -> Int
gfromEnum = conIndex . unSOP . from
where
conIndex :: NS f xs -> Int
conIndex (Z _) = 0
conIndex (S i) = 1 + conIndex i
The function from turns a value into a sum-of-products representation, and unSOP strips the outer constructor. We then have a sum structure to traverse. The NS datatype representing n-ary sums has constructors Z and S that indicate exactly which constructor is being used, so we can simply traverse and count.
gtoEnum :: (Generic a, IsEnumType a) => Int -> a
gtoEnum i =
to (SOP (apInjs_NP (hcpure (Proxy # ((~) '[])) Nil) !! i))
Here, the apInjs_NP (hcpure ...) call produces the representations of
empty constructor applications for all constructors of the datatype. Unlike the gfromEnum function, this actually makes use of the IsEnumType constraint to be type correct (because we rely on the fact that none of the constructors take any arguments). We then selected the i-th constructor out of the list and turn it back from the generic representation to the actual type by applying first SOP and then to.
To apply it to your sample type, you have to instantiate it to both GHC's and generic-sop's Generic classes (or you can use TH for this, too):
data Foo = Foo | Bar | Baz | Quux
deriving (Show, Eq, GHC.Generic)
instance Generic Foo
Then you can test it:
GHCi> gfromEnum Baz
2
GHCi> gtoEnum 2 :: Foo
Baz
If you want, you can make gfromEnum and gtoEnum the default definitions for an Enum-like class, just as with GHC Generics.

Resources