unsafeVacuous in Data.Void.Unsafe and .# and #. in Data.Profunctor.Unsafe both warn about the dangers of using those functions with functors/profunctors that are GADTs. Some dangerous examples are obvious:
data Foo a where
P :: a -> Foo a
Q :: Foo Void
instance Functor Foo where
fmap f (P x) = P (f x)
fmap f Q = P (f undefined)
Here, unsafeVacuous Q will produce a Q constructor with a bogus type.
This example isn't very troubling because it doesn't look even remotely like a sensible Functor instance. Are there examples that do? In particular, would it be possible to construct useful ones that obey the functor/profunctor laws when manipulated only using their public API, but break horribly in the face of these unsafe functions?
I don't believe there's any true functor where unsafeVacuous would cause a problem. But if you write a bad Functor you can make your own unsafeCoerce, which means it should to labeled with {-# LANGUAGE Unsafe #-}. There was an issue about it in void.
Here's an unsafeCoerce I came up with
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Void
import Data.Void.Unsafe
type family F a b where
F a Void = a
F a b = b
data Foo a b where
Foo :: F a b -> Foo a b
instance Functor (Foo a) where
fmap = undefined
unsafeCoerce :: forall a b. (F a b ~ b) => a -> b
unsafeCoerce a = (\(Foo b) -> b) $ (unsafeVacuous (Foo a :: Foo a Void) :: Foo a b)
main :: IO ()
main = print $ (unsafeCoerce 'a' :: Int)
which prints 97.
Related
I'm trying to use type class to simulate ad-hoc polymorphism and solve generic cases involving higher kinded types and so far can't figure out the correct solution.
What I'm looking for is to define something similar to:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
infixl 0 >>>
-- | Type class that allows applying a value of type #fn# to some #m a#
class Apply m a fn b | a fn -> b where
(>>>) :: m a -> fn -> m b
-- to later use it in following manner:
(Just False) >>> True -- same as True <$ ma
(Just True) >>> id -- same as id <$> ma
Nothing >>> pure Bool -- same as Nothing >>= const $ pure Bool
(Just "foo") >>> (\a -> return a) -- same as (Just "foo") >>= (\a -> return a)
So far I've tried multiple options, none of them working.
Just a straight forward solution obviously fails:
instance (Functor m) => Apply m a b b where
(>>>) m b = b <$ m
instance (Monad m) => Apply m a (m b) b where
(>>>) m mb = m >>= const mb
instance (Functor m) => Apply m a (a -> b) b where
(>>>) m fn = fmap fn m
instance (Monad m, a' ~ a) => Apply m a (a' -> m b) b where
(>>>) m fn = m >>= fn
As there are tons of fundep conflicts (all of them) related to the first instance that gladly covers all the cases (duh).
I couldn't work out also a proper type family approach:
class Apply' (fnType :: FnType) m a fn b | a fn -> b where
(>>>) :: m a -> fn -> m b
instance (Functor m) => Apply' Const m a b b where
(>>>) m b = b <$ m
instance (Monad m) => Apply' ConstM m a (m b) b where
(>>>) m mb = m >>= const mb
instance (Functor m, a ~ a') => Apply' Fn m a (a' -> b) b where
(>>>) m mb = m >>= const mb
instance (Functor m, a ~ a') => Apply' Fn m a (a' -> m b) b where
(>>>) m fn = m >>= fn
data FnType = Const | ConstM | Fn | FnM
type family ApplyT a where
ApplyT (m a) = ConstM
ApplyT (a -> m b) = FnM
ApplyT (a -> b) = Fn
ApplyT _ = Const
Here I have almost the same issue, where the first instance conflicts with all of them through fundep.
The end result I want to achieve is somewhat similar to the infamous magnet pattern sometimes used in Scala.
Update:
To clarify the need for such type class even further, here is a somewhat simple example:
-- | Monad to operate on
data Endpoint m a = Endpoint { runEndpoint :: Maybe (m a) } deriving (Functor, Applicative, Monad)
So far there is no huge need to have mentioned operator >>> in place, as users might use the standard set of <$ | <$> | >>= instead. (Actually, not sure about >>= as there is no way to define Endpoint in terms of Monad)
Now to make it a bit more complex:
infixr 6 :::
-- | Let's introduce HList GADT
data HList xs where
HNil :: HList '[]
(:::) :: a -> HList as -> HList (a ': as)
-- Endpoint where a ~ HList
endpoint :: Endpoint IO (HList '[Bool, Int]) = pure $ True ::: 5 ::: HNil
-- Some random function
fn :: Bool -> Int -> String
fn b i = show b ++ show i
fn <$> endpoint -- doesn't work, as fn is a function of a -> b -> c, not HList -> c
Also, imagine that the function fn might be also defined with m String as a result. That's why I'm looking for a way to hide this complexity away from the API user.
Worth mentioning, I already have a type class to convert a -> b -> c into HList '[a, b] -> c
If the goal is to abstract over HLists, just do that. Don't muddle things by introducing a possible monad wrapper at every argument, it turns out to be quite complicated indeed. Instead do the wrapping and lifting at the function level with all the usual tools. So:
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
data HList a where
HNil :: HList '[]
(:::) :: x -> HList xs -> HList (x : xs)
class ApplyArgs args i o | args i -> o, args o -> i where
apply :: i -> HList args -> o
instance i ~ o => ApplyArgs '[] i o where
apply i _ = i
instance (x ~ y, ApplyArgs xs i o) => ApplyArgs (x:xs) (y -> i) o where
apply f (x ::: xs) = apply (f x) xs
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.
I am using makeFields from lens to generate fields overloaded for various structures. I would like to use these fields at one with multiple structures while having to state which field I want to use only once. It would look like this:
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
import Control.Lens
data A = A
{ _aX :: String
, _aY :: String
}
makeFields ''A
data B = B
{ _bX :: String -> Char
, _bY :: String -> Bool
}
makeFields ''B
-- x can get _aX from an A and _bX from a B
a :: A
a = undefined
b :: B
b = undefined
q :: (Getter A String) AND (Getter B (String -> a)) -> a
q lens = (b^.lens) (a^.lens)
Which type should I give q? I tried letting GHC infer the types, but that failed.
To decide what is to be done, we need to know what the types of your (makeField-generated) fields are:
GHCi> :t x
x :: (HasX s a, Functor f) => (a -> f a) -> s -> f s
So the abstraction covering all your x-bearing types (the abstraction I was whining about before noticing you were using makeFields) is a multi-parameter type class HasX, and similarly for the other fields. That gives us enough to use x with different types in a single implementation:
-- Additional extension required: FlexibleContexts
-- Note that GHC is able to infer this type.
qx :: (HasX t (a -> b), HasX s a) => t -> s -> b
qx t s = (t ^. x) (s ^. x)
GHCi> import Data.Maybe
GHCi> let testA = A "foo" "bar"
GHCi> let testB = B (fromMaybe 'ΓΈ' . listToMaybe) null
GHCi> qx testB testA
'f'
That, however, is not quite what you asked for. You wanted something like:
q xOrY b a = (b^.xOrY) (a^.xOrY)
Achieving that, however, requires abstracting over the classes HasX, HasY, etc. Doing so is, in fact, somewhat feasible thanks to the ConstraintKinds extension, as demonstrated in Could we abstract over type classes? Here it goes:
-- Additional extensions required: ConstraintKinds, ScopedTypeVariables
-- Additional import required: Data.Proxy
-- GHC cannot infer this type.
q :: forall h s t a b. (h t (a -> b), h s a) => Proxy a -> Proxy h
-> (forall u c. h u c => Getting c u c) -> t -> s -> b
q _ _ l t s =
(t ^. (l :: Getting (a -> b) t (a -> b))) (s ^. (l :: Getting a s a))
GHCi> q (Proxy :: Proxy String) (Proxy :: Proxy HasX) x testB testA
'f'
The first proxy, which determines the intermediate type, is necessary unless you give up this bit of generality and replace a by String. Additionally, you have to specify the field twice, both by passing the getter as an argument and through the second proxy. I am not at all convinced that this second solution is worth the trouble -- the extra boilerplate of having to define qx, qy, etc. looks quite a bit less painful than all the circuitousness involved here. Still, if any of you who are reading this would like to suggest an improvement, I'm all ears.
It's possible to mix classes with lenses to simulate overloaded record fields, up to a point. See, for example, makeFields in Control.Lens.TH. I'm trying to figure out if there's a nice way to reuse the same name as a lens for some types and a traversal for others. Notably, given a sum of products, each product can have lenses, which will degrade to traversals of the sum. The simplest thing I could think of was this**:
First try
class Boo booey where
type Con booey :: (* -> *) -> Constraint
boo :: forall f . Con booey f => (Int -> f Int) -> booey -> f booey
This works fine for simple things, like
data Boop = Boop Int Char
instance Boo Boop where
type Con Boop = Functor
boo f (Boop i c) = (\i' -> Boop i' c) <$> f i
But it falls on its face as soon as you need anything more complicated, like
instance Boo boopy => Boo (Maybe boopy) where
which should be able to produce a Traversal regardless of the choice of underlying Boo.
Second try
The next thing I tried, which sort of works, is to constrain the Con family. This gets kind of gross. First, change the class:
class LTEApplicative c where
lteApplicative :: Applicative a :- c a
class LTEApplicative (Con booey) => Boo booey where
type Con booey :: (* -> *) -> Constraint
boo :: forall f . Con booey f => (Int -> f Int) -> booey -> f booey
This makes Boo instances carry around explicit evidence that their boo produces a Traversal' booey Int. Some more stuff:
instance LTEApplicative Applicative where
lteApplicative = Sub Dict
instance LTEApplicative Functor where
lteApplicative = Sub Dict
-- flub :: Boo booey => Traversal booey booey Int Int
flub :: forall booey f . (Boo booey, Applicative f) => (Int -> f Int) -> booey -> f booey
flub = case lteApplicative of
Sub (Dict :: Dict (Con booey f)) -> boo
instance Boo boopy => Boo (Maybe boopy) where
type Con (Maybe boopy) = Applicative
boo _ Nothing = pure Nothing
boo f (Just x) = Just <$> hum f x
where hum :: Traversal' boopy Int
hum = flub
And the base Boop example works unchanged.
Why this still sucks
We now have boo producing a Lens or Traversal under appropriate circumstances, and we can always use it as a Traversal, but every time we want to do so, we have to first drag in the evidence that it really is one. This is, of course, far too inconvenient for the purpose of implementing overloaded record fields! Is there any nicer way?
** This code compiles with the following (may not be minimal):
{-# LANGUAGE PolyKinds, TypeFamilies,
TypeOperators, FlexibleContexts,
ScopedTypeVariables, RankNTypes,
KindSignatures #-}
import Control.Lens
import Data.Constraint
The following has worked for me before:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
import Control.Lens
data Boop = Boop Int Char deriving (Show)
class HasBoo f s where
boo :: LensLike' f s Int
instance Functor f => HasBoo f Boop where
boo f (Boop a b) = flip Boop b <$> f a
instance (Applicative f, HasBoo f s) => HasBoo f (Maybe s) where
boo = traverse . boo
It can be also scaled to polymorphic fields, if we make sure to enforce all the relevant functional dependencies (just like here). Leaving an overloaded field completely polymorphic is rarely useful or a good idea; I illustrate that case though because from there one can always monomorphize as necessary (or we can constrain polymorphic fields, for example a name field to IsString).
{-# LANGUAGE
UndecidableInstances, MultiParamTypeClasses,
FlexibleInstances, FunctionalDependencies, TemplateHaskell #-}
import Control.Lens
data Foo a b = Foo {_fooFieldA :: a, _fooFieldB :: b} deriving Show
makeLenses ''Foo
class HasFieldA f s t a b | s -> a, t -> b, s b -> t, t a -> s where
fieldA :: LensLike f s t a b
instance Functor f => HasFieldA f (Foo a b) (Foo a' b) a a' where
fieldA = fooFieldA
instance (Applicative f, HasFieldA f s t a b) => HasFieldA f (Maybe s) (Maybe t) a b where
fieldA = traverse . fieldA
One can also go a bit wild and use a single class for all "has" functionality:
{-# LANGUAGE
UndecidableInstances, MultiParamTypeClasses,
RankNTypes, TypeFamilies, DataKinds,
FlexibleInstances, FunctionalDependencies,
TemplateHaskell #-}
import Control.Lens hiding (has)
import GHC.TypeLits
import Data.Proxy
class Has (sym :: Symbol) f s t a b | s sym -> a, sym t -> b, s b -> t, t a -> s where
has' :: Proxy sym -> LensLike f s t a b
data Foo a = Foo {_fooFieldA :: a, _fooFieldB :: Int} deriving Show
makeLenses ''Foo
instance Functor f => Has "fieldA" f (Foo a) (Foo a') a a' where
has' _ = fooFieldA
With GHC 8, one can add
{-# LANGUAGE TypeApplications #-}
and avoid the proxies:
has :: forall (sym :: Symbol) f s t a b. Has sym f s t a b => LensLike f s t a b
has = has' (Proxy :: Proxy sym)
instance (Applicative f, Has "fieldA" f s t a b) => Has "fieldA" f (Maybe s) (Maybe t) a b where
has' _ = traverse . has #"fieldA"
Examples:
> Just (Foo 0 1) ^? has #"fieldA"
Just 0
> Foo 0 1 & has #"fieldA" +~ 10
Foo {_fooFieldA = 10, _fooFieldB = 1}
Record or simple ADT in haskell are pretty much equivalent to boxed tuples.
Is there a way (ideally some fancy extensions or a lib from the haksell platform) which allow conversion between such type and tuples ?
I'm (fairly ) new to haskell and I'm trying to build some reporting tool in Haskell. This involves reading/writing csv files and database tables. Things are pretty much straight forward using tuples, but involve a bit of boiler plate when using plain class.
The boilerplate seams nearly identical in both way, but I didn't find a nice way to do it only once, except maybe from doing a conversion (data <-> tuple) and use the native conversion from tuple to CSV/table.
Update
All the answer I got back so far, assumes that I need something totally generic and I want tuple.
I don't want tuple, I have tuple and I don't want them, therefore the need to convert them.
In fact I just want to reduce the boiler plate (to 0 :-)) but I don't need necessarily the function(s) to have the same name for every types.
For example I can easily convert a tuple to anything by uncurrying one of its constructors.
The problem is I need uncurryN which I can't find anywhere (except in a template haskell tutorial).
The reverse is harder to do.
I'm not asking for a solution (althout all the answers I got are greats because I'm not familiar whith the different way of meta-programming in Haskell) but more, as I don't like to reinvent the wheel, if the wheel existed already (for example this uncurryN, could have been written by hand till 20 and packed in nice package)
Updated2
Apparently a uncurry package exists, but it stills solves half the problem.
You might want to look at GHC.Generics. It basically encodes each ADT as products ((,)) and sums (Either). As an example, here is how you could show this representation using generics:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
import GHC.Generics
class Tuple p where
showRepresentation :: p -> String
default showRepresentation :: (Generic p, GTuple (Rep p)) => p -> String
showRepresentation = gshowRepresentation . from
class GTuple p where
gshowRepresentation :: p x -> String
instance Tuple k => GTuple (K1 i k) where
gshowRepresentation (K1 t) = showRepresentation t
instance GTuple f => GTuple (M1 i c f) where
gshowRepresentation (M1 f) = gshowRepresentation f
instance (GTuple f, GTuple g) => GTuple (f :*: g) where
gshowRepresentation (f :*: g) = gshowRepresentation f ++ " * " ++ gshowRepresentation g
-- Some instances for the "primitive" types
instance Tuple Int where showRepresentation = show
instance Tuple Bool where showRepresentation = show
instance Tuple () where showRepresentation = show
--------------------------------------------------------------------------------
data Example = Example Int () Bool deriving Generic
instance Tuple Example
main :: IO ()
main = putStrLn $ showRepresentation $ Example 3 () False
-- prints: 3 * () * False
You can find more documentation in the GHC.Generics module. I also found the paper about it, A Generic Deriving Mechanism for Haskell to be quite readable (it was one of the few papers I read).
The lens library, in modules Control.Lens.Iso and Control.Lens.Wrapped, has a few utilities that make working with such conversions easier. Unfortunately, at the moment the Template Haskell machinery for such cases does not handle records, only newtypes, so you'll have to define the instances yourself. For example:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Lens
data Foo = Foo { baz :: Int, bar :: Int } deriving Show
instance Wrapped Foo where
type Unwrapped Foo = (Int,Int)
_Wrapped' = iso (\(Foo baz' bar') -> (baz',bar')) (\(baz',bar') -> Foo baz' bar')
Now we can wrap and unwrap easily:
*Main> (2,3) ^. _Unwrapped' :: Foo
Foo {baz = 2, bar = 3}
*Main> Foo 2 3 ^. _Wrapped'
(2,3)
We can also modify a Foo using a function that works on the tuple:
*Main> over _Wrapped' (\(x,y)->(succ x,succ y)) $ Foo 2 5
Foo {baz = 3, bar = 6}
And the reverse:
*Main> under _Wrapped' (\(Foo x y)->(Foo (succ x) (succ y))) $ (2,5)
(3,6)
If you want real n-tuples (and not just some other data that is semantically equivalent) it's going to be cumbersome without Template Haskell.
For example, if you want to convert
data Foo = Foo Int String Int
data Bar = Bar String String Int Int
into
type FooTuple = (Int, String, Int)
type BarTuple = (String, String, Int, Int)
both GHC.Generics and SYB will be problematic because the result type needs to be different depending on the fields of the datatype. Even though both are calle "tuples", (Int, String, Int) and (String, String, Int, Int) are completely separate types and there are no convenient ways to work with n-arity tuples in a generic fashion. Here's one way to achieve the above using GHC.Generics:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DeriveGeneric #-}
-- Generic instance to turn generic g x into some n-tuple whose exact
-- type depends on g.
class GTuple g where
type NTuple g
gtoTuple :: g x -> NTuple g
-- Unwarp generic metadata
instance GTuple f => GTuple (M1 i c f) where
type NTuple (M1 i c f) = NTuple f
gtoTuple = gtoTuple . unM1
-- Turn individual fields into a Single type which we need to build up
-- the final tuples.
newtype Single x = Single x
instance GTuple (K1 i k) where
type NTuple (K1 i k) = Single k
gtoTuple (K1 x) = Single x
-- To combine multiple fields, we need a new Combine type-class.
-- It can take singular elements or tuples and combine them into
-- a larger tuple.
--
class Combine a b where
type Combination a b
combine :: a -> b -> Combination a b
-- It's not very convenient because it needs a lot of instances for different
-- combinations of things we can combine.
instance Combine (Single a) (Single b) where
type Combination (Single a) (Single b) = (a, b)
combine (Single a) (Single b) = (a, b)
instance Combine (Single a) (b, c) where
type Combination (Single a) (b, c) = (a, b, c)
combine (Single a) (b, c) = (a, b, c)
instance Combine (a,b) (c,d) where
type Combination (a,b) (c,d) = (a,b,c,d)
combine (a,b) (c,d) = (a,b,c,d)
-- Now we can write the generic instance for constructors with multiple
-- fields.
instance (Combine (NTuple a) (NTuple b), GTuple a, GTuple b) => GTuple (a :*: b) where
type NTuple (a :*: b) = Combination (NTuple a) (NTuple b)
gtoTuple (a :*: b) = combine (gtoTuple a) (gtoTuple b)
-- And finally the main function that triggers the tuple conversion.
toTuple :: (Generic a, GTuple (Rep a)) => a -> NTuple (Rep a)
toTuple = gtoTuple . from
-- Now we can test that our instances work like they should:
data Foo = Foo Int String Int deriving (Generic)
data Bar = Bar String String Int Int deriving (Generic)
fooTuple = toTuple $ Foo 1 "foo" 2
barTuple = toTuple $ Bar "bar" "asdf" 3 4
The above works but it requires a lot of work (and I couldn't quickly figure out
if it could be done without using UndecidableInstances).
Now what you really want to do is probably just skip the tuples and use generics
to convert directly to CSV. I'm assuming you are using csv-conduit and want to generate instances of the ToRecord type-class.
Here's an example of that
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
import Data.ByteString (ByteString)
import Data.CSV.Conduit.Conversion
class GRecord g where
gToRecord :: g x -> [ByteString]
instance GRecord f => GRecord (M1 i c f) where
gToRecord = gToRecord . unM1
instance ToField k => GRecord (K1 i k) where
gToRecord (K1 x) = [toField x]
instance (GRecord a, GRecord b) => GRecord (a :*: b) where
gToRecord (a :*: b) = gToRecord a ++ gToRecord b
genericToRecord :: (Generic a, GRecord (Rep a)) => a -> Record
genericToRecord = record . gToRecord . from
And now you can easily make instances for your custom types.
data Foo = Foo Int String Int deriving (Generic)
data Bar = Bar String String Int Int deriving (Generic)
instance ToRecord Foo where
toRecord = genericToRecord
instance ToRecord Bar where
toRecord = genericToRecord
In response to your updated question: you might be interested in the tuple package (and especially Curry) which contains implementations for uncurryN and curryN for tuples up to 15 elements.
In some cases, you can use unsafeCoerce. The name of the function should be quite a clear warning to be very careful. Particularly, the behavior is dependent on the compiler and even compiler version.
data Bar = Bar Text Text
tupleToBar :: (Text, Text) -> Bar
tupleToBar = unsafeCoerce
The function _Ctor in generic-lens converts any record value to a tuple and vice versa with no template haskell.