Are there examples of generics-sop that mimic SYB's everywhere/mkT behavior?
What I'm attempting to do, but not seeing how to do it successfully, is replace the everywhere (mkT fixupSymbol) in main with an equivalent Generics.SOP construction, i.e., use Generics.SOP to recurse into the product (I (AbsAddr value)) and replace it with (I (SymAddr label)).
I could pass the symbol table to gformatOperands, polluting the formatOperands signature. That seems suboptimal.
Without fixupSymbol, the output would look like:
LD B, 0x0000
LD C, 0x1234
CALL 0x4567
Resolving addresses to symbolic labels:
gensop % stack ghci
Using main module: 1. Package `gensop' component exe:gensop with main-is file: <...>/Main.hs
gensop-0.1: configure (exe)
Configuring gensop-0.1...
gensop-0.1: initial-build-steps (exe)
Configuring GHCi with the following packages: gensop
GHCi, version 8.6.3: http://www.haskell.org/ghc/ :? for help
[1 of 1] Compiling Main ( <...>/Main.hs, interpreted )
*Main> main
LD B, 0x0000
LD C, label1
CALL label2
*Main>
Cut down version of code:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Main where
import Data.Data
import Data.Foldable (foldl)
import Data.Word (Word8, Word16)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Text.Printf
import Generics.SOP
import Generics.SOP.TH (deriveGeneric)
import Data.Generics.Aliases (mkT)
import Data.Generics.Schemes (everywhere)
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as H
import Data.Sequence (Seq, (|>))
import qualified Data.Sequence as Seq
type Z80addr = Word16
type Z80word = Word8
class Z80operand x where
formatOperand :: x -> Text
main :: IO()
main = mapM_ T.putStrLn (foldl printIns Seq.empty $ everywhere (mkT fixupSymbol) insnSeq)
-- -------------------------------------------------^ Does this have a Generics.SOP equivalent?
where
printIns accum ins = accum |> T.concat ([mnemonic, gFormatOperands] <*> [ins])
mnemonic (LD _) = "LD "
mnemonic (CALL _) = "CALL "
-- Generics.SOP: Fairly straightforward
gFormatOperands {-elt-} =
T.intercalate ", " . hcollapse . hcmap disOperandProxy (mapIK formatOperand) . from {-elt-}
where
disOperandProxy = Proxy :: Proxy Z80operand
-- Translate an absolute address, generally hidden inside an instruction operand, into a symbolic address
-- if present in the symbol table.
fixupSymbol addr#(AbsAddr absAddr) = maybe addr SymAddr (absAddr `H.lookup` symtab)
fixupSymbol other = other
insnSeq :: Seq Z80instruction
insnSeq = Seq.singleton (LD (Reg8Imm B 0x0))
|> (LD (Reg8Indirect C (AbsAddr 0x1234)))
|> (CALL (AbsAddr 0x4567))
symtab :: HashMap Z80addr Text
symtab = H.fromList [ (0x1234, "label1"), (0x4567, "label2")]
-- | Symbolic and absolute addresses. Absolute addresses can be translated into symbolic
-- labels.
data SymAbsAddr = AbsAddr Z80addr | SymAddr Text
deriving (Eq, Ord, Typeable, Data)
data Z80reg8 = A | B | C
deriving (Eq, Ord, Typeable, Data)
-- | Cut down version of the Z80 instruction set
data Z80instruction = LD OperLD | CALL SymAbsAddr
deriving (Eq, Ord, Typeable, Data)
-- | Load operands
data OperLD = Reg8Imm Z80reg8 Z80word | Reg8Indirect Z80reg8 SymAbsAddr
deriving (Eq, Ord, Typeable, Data)
$(deriveGeneric ''SymAbsAddr)
$(deriveGeneric ''Z80reg8)
$(deriveGeneric ''Z80instruction)
$(deriveGeneric ''OperLD)
instance Z80operand Z80word where
formatOperand word = T.pack $ printf "0x%04x" word
instance Z80operand SymAbsAddr where
formatOperand (AbsAddr addr) = T.pack $ printf "0x04x" addr
formatOperand (SymAddr label) = label
instance Z80operand Z80reg8 where
formatOperand A = "A"
formatOperand B = "B"
formatOperand C = "C"
instance Z80operand OperLD where
formatOperand (Reg8Imm reg imm) = T.concat [formatOperand reg, ", ", formatOperand imm]
formatOperand (Reg8Indirect reg addr) = T.concat [formatOperand reg, ", ", formatOperand addr]
The gensop.cabal file:
cabal-version: >= 1.12
name: gensop
version: 0.1
build-type: Simple
author: scooter-me-fecit
description: No description.
license: GPL-3
executable gensop
default-language: Haskell2010
main-is: Main.hs
build-depends:
base,
containers,
bytestring,
generics-sop,
syb,
text,
unordered-containers
default-extensions:
OverloadedStrings,
FlexibleInstances
ghc-options: -Wall
generics-sop provides no equivalents for recursive traversal schemes, such as these functions. If you need to deal with recursion in this library, the possible solution is to implement them. Although, defining such functions in SOP is related to some difficulties because it has a core generic view on data that doesn't distinguish recursive nodes from leaves. Recursion in this setting can be managed using closed type families (CTF) and some type class machinery. Closed type families allow you:
to implement the type-safe cast, which is needed for defining mkT,
to resolve the cases of recursive and non-recursive nodes—different
instances of a type class—which otherwise overlap. (Another option
is using pragmas for overlapping instances, a recent GHC feature;
there is, however, some bias about overlapping instances in the
Haskell community, so this solution is often considered as
undesired.)
Using CTF for treating recursion has been described in an unpublished paper “Handling Recursion in Generic Programming Using Closed Type Families”, which uses the generics-sop library as a case study; it provides examples of defining recursive schemes in SOP.
SYB's everywhere supports families of mutually recursive datatypes. The following implementation allows to specify them as type-level lists.
{-# LANGUAGE DeriveGeneric, TypeFamilies, DataKinds,
TypeApplications, ScopedTypeVariables, MultiParamTypeClasses,
ConstraintKinds, FlexibleContexts, AllowAmbiguousTypes,
FlexibleInstances, UndecidableInstances,
UndecidableSuperClasses, TypeOperators, RankNTypes #-}
import Generics.SOP
import Generics.SOP.NS
import GHC.Exts (Constraint)
import Data.Type.Equality
type family Equal a x :: Bool where
Equal a a = 'True
Equal _ _ = 'False
class DecideEq (eq :: Bool) (a :: *) (b :: *) where
decideEq :: Maybe (b :~: a)
instance a ~ b => DecideEq True a b where
decideEq = Just Refl
instance DecideEq False a b where
decideEq = Nothing
type ProofCast a b = DecideEq (Equal a b) a b
castEq :: forall a b. ProofCast a b => b -> Maybe a
castEq t = (\d -> castWith d t) <$> decideEq #(Equal a b)
type Transform a b = (Generic a, Generic b, ProofCast a b, ProofCast b a)
mkT :: Transform a b => (a -> a) -> b -> b
mkT f x = maybe x id $ castEq =<< f <$> castEq x
type family In (a :: *) (fam :: [*]) :: Bool where
In a ([a] ': fam) = 'True
In [a] (a ': fam) = 'True
In a (a ': fam) = 'True
In a (_ ': fam) = In a fam
In _ '[] = 'False
class CaseEverywhere' (inFam :: Bool) (c :: * -> Constraint)
(fam :: [*]) (x :: *) (y :: *) where
caseEverywhere' :: (forall b . c b => b -> b) -> I x -> I y
instance c x => CaseEverywhere' 'False c fam x x where
caseEverywhere' f = I . f . unI
instance (c x, Everywhere x c fam) => CaseEverywhere' 'True c fam x x where
caseEverywhere' f = I . f . everywhere #fam #c f . unI
class CaseEverywhere' (In x fam) c fam x y => CaseEverywhere c fam x y
instance CaseEverywhere' (In x fam) c fam x y => CaseEverywhere c fam x y
caseEverywhere :: forall c fam x y . CaseEverywhere c fam x y
=> (forall b . c b => b -> b) -> I x -> I y
caseEverywhere = caseEverywhere' #(In x fam) #c #fam
type Everywhere a c fam =
(Generic a, AllZip2 (CaseEverywhere c fam) (Code a) (Code a))
everywhere :: forall fam c a . Everywhere a c fam
=> (forall b . c b => b -> b) -> a -> a
everywhere f = to . everywhere_SOP . from
where
everywhere_SOP = trans_SOP (Proxy #(CaseEverywhere c fam)) $
caseEverywhere #c #fam f
Usage
First, this can be examined with a small-scale example taken from the SYB paper. The implemented SOP-based everywhere, as compared to SYB's one, additionally takes two type arguments, passed through the explicit type application. The first one specifies a family of mutually recursive datatypes as a type list. The traversal will treat as recursive only those nodes whose types are specified in that list. The second argument is needed to provide a compiler with a ‘proof’ object for type-cast. The T synonym for the Transform constraint serves to allow its partial application.
data Company = C [Dept]
data Dept = D Name Manager [SubUnit]
data SubUnit = PU Employee | DU Dept
data Employee = E Person Salary
data Person = P Name Address
data Salary = S Float
type Manager = Employee
type Name = String
type Address = String
class Transform a b => T a b
instance Transform a b => T a b
type CompanyF = '[Company, Dept, SubUnit, Employee]
increase :: Float -> Company -> Company
increase k = everywhere #CompanyF #(T Salary) (mkT (incS k))
incS :: Float -> Salary -> Salary
incS k (Sal s) = Sal (s * (1 + k))
The defined everywhere / mkT functions are ready for using in your code, but it misses some Generic instances. To apply everywhere to insnSeq, you need a Generic (Seq Z80instruction) instance. Yet you can't obtain it, because the Data.Sequence module doesn't export the internal representation of it. A possible fix is applying fmap to the sequence. So now you can write:
{-# LANGUAGE TypeApplications #-}
...
type Z80 = '[SymAbsAddr, Z80reg8, Z80instruction, OperLD]
main :: IO()
main = mapM_ T.putStrLn (foldl printIns Seq.empty $
fmap (everywhere #Z80 #(T SymAbsAddr) (mkT fixupSymbol)) insnSeq)
You should provide the Generic instances for all types of nodes that this traverses, recursive and non-recursive. So next, this demands the Generic instances for Word8, Word16, and Text. While the Generic Text instance can be generated via deriveGeneric, the others can't, because of their special GHC representation. So you'll have to do it manually; this definition is straightforward:
$(deriveGeneric ''Text)
instance Generic Word8 where
type Code Word8 = '[ '[Word8]]
from x = SOP (Z (I x :* Nil))
to (SOP ((Z (I x :* Nil)))) = x
instance Generic Word16 where
type Code Word16 = '[ '[Word16]]
from x = SOP (Z (I x :* Nil))
to (SOP ((Z (I x :* Nil)))) = x
This code is boilerplate, but the newest GHC extension DerivingVia could nicely simplify this, reducing the second definition. Hopefully, this useful feature will be improved with possibilities for standalone deriving, so it will be possible to say instead:
deriving via Word8 instance Generic Word16
The whole code now works well, and main yields the expected result.
Related
How would I go about deriving the function
getField :: (Generic a, HasDatatypeInfo a) => Proxy (name :: Symbol) -> a -> b
to project a field from an arbitrary record using a type-level string (Symbol), using the generics-sop library?
This is similar to Retrieving record function in generic SOP, but I have the following problems:
The OP does not explain how to go the last mile to get the signature I desire.
The OP defines complex special-purpose helper types, which I am keen to avoid
The given solution only errors out at runtime, but compile-time matching should be possible, since a type-level DataTypeInfo is provided through the DatatypeInfoOf type family (nice to have, but not necessary).
The lens-sop package also seems to do something similar, but I can't work out how to make it work for me.
I would also prefer a solution that uses the IsProductType typeclass.
I know this is a mess of an answer and not really what you were looking for, but it's the best I can do right now. Note that this works for both product types and sum types where all the constructors have the specified field name.
I think this could likely be simplified somewhat by separating the name lookup from the rest of the product handling. That is: use the datatype info to calculate the field number (as a unary natural), then use that number to dig through the code. Unfortunately, generics-sop doesn't seem to have really wonderful facilities for working with list zipping, so I ended up doing a lot "by hand".
{-# language EmptyCase, GADTs, TypeFamilies, DataKinds, TypeOperators, RankNTypes #-}
{-# language UndecidableInstances, UndecidableSuperClasses #-}
{-# language AllowAmbiguousTypes, TypeApplications, MultiParamTypeClasses,
FlexibleContexts, FlexibleInstances, MagicHash, UnboxedTuples, ScopedTypeVariables #-}
{-# language ConstraintKinds #-}
{-# OPTIONS_GHC -Wall #-}
module Data.Proj where
import Data.Kind (Type, Constraint)
import Generics.SOP
import Generics.SOP.Type.Metadata as GST
import GHC.TypeLits
import Data.Type.Equality (type (==))
-- This is what you were looking for, but slightly more flexible.
genericPrj :: forall s b a.
( Generic a
, HasFieldNS s b (GetConstructorInfos (DatatypeInfoOf a)) (Code a))
=> a -> b
genericPrj a = case genericPrj# #s a of (# b #) -> b
-- This version lets you force the *extraction* of a field without
-- forcing the field itself.
genericPrj# :: forall s b a.
( Generic a
, HasFieldNS s b (GetConstructorInfos (DatatypeInfoOf a)) (Code a))
=> a -> (# b #)
genericPrj# a = case from a of
SOP xs -> extraction #s #b #(GetConstructorInfos (DatatypeInfoOf a)) #(Code a) xs
-- | Extract info about the constructor(s) from 'GST.DatatypeInfo'.
type family GetConstructorInfos (inf :: GST.DatatypeInfo) :: [GST.ConstructorInfo] where
GetConstructorInfos ('GST.ADT _ _ infos _) = infos
GetConstructorInfos ('GST.Newtype _ _ info) = '[info]
class HasFieldNS (s :: Symbol) b (cis :: [GST.ConstructorInfo]) (code :: [[Type]]) where
extraction :: NS (NP I) code -> (# b #)
instance HasFieldNS s b cis '[] where
extraction x = case x of
instance (HasFieldNP' s b r c, HasFieldNS s b cis cs, rec ~ 'GST.Record q r, VerifyRecord rec)
=> HasFieldNS s b (rec ': cis) (c ': cs) where
extraction (Z x) = extractIt #s #b #rec #c x
extraction (S x) = extraction #s #b #cis #cs x
type family VerifyRecord rec :: Constraint where
VerifyRecord ('GST.Record _ _) = ()
VerifyRecord _ = TypeError ('Text "Constructor is not in record form.")
-- | Given info about a constructor, a list of its field types, and the name and
-- type of a field, produce an extraction function.
class HasFieldNP (s :: Symbol) b (ci :: GST.ConstructorInfo) (fields :: [Type]) where
extractIt :: NP I fields -> (# b #)
instance (HasFieldNP' s b fi fields, ci ~ 'GST.Record _cn fi)
=> HasFieldNP s b ci fields where
extractIt = extractIt' #s #_ #fi
class HasFieldNP' (s :: Symbol) b (fi :: [GST.FieldInfo]) (fields :: [Type]) where
extractIt' :: NP I fields -> (# b #)
class TypeError ('Text "Can't find field " ':<>: 'ShowType s)
=> MissingField (s :: Symbol) where
impossible :: a
instance MissingField s => HasFieldNP' s b fi '[] where
extractIt' = impossible #s ()
instance HasFieldNP'' s b (fi == s) field fis fields =>
HasFieldNP' s b ('GST.FieldInfo fi ': fis) (field ': fields) where
extractIt' = extractIt'' #s #b #(fi == s) #field #fis #fields
class HasFieldNP'' (s :: Symbol) b (match :: Bool) (field :: Type) (fis :: [GST.FieldInfo]) (fields :: [Type]) where
extractIt'' :: NP I (field ': fields) -> (# b #)
instance b ~ field => HasFieldNP'' _s b 'True field fis fields where
extractIt'' (I x :* _) = (# x #)
instance (HasFieldNP' s b fis fields) => HasFieldNP'' s b 'False _field fis fields where
extractIt'' (_ :* fields) = extractIt' #s #b #fis fields
Examples
data Foo
= Foo {blob :: Int, greg :: String}
| Bar {hello :: Char, blob :: Int}
deriveGeneric ''Foo
genericPrj #"blob" (Foo 12 "yo") ===> 12
genericPrj #"blob" (Bar 'x' 5) ===> 5
genericPrj# #"blob" (Bar 'x' 5) ===> (# 5 #)
myAbsurd :: Void -> a
myAbsurd = genericPrj #"whatever"
data Booby a
= Booby {foo :: a}
| Bobby {bar :: a}
deriveGeneric ''Booby
genericPrj #"foo" (Booby 'a')
-- Type error because Bobby has no foo field
As of version 0.1.1.0, records-sop provides this function:
getField :: forall s a b ra. (IsRecord a ra, IsElemOf s b ra) => a -> b
which needs the field name supplied as a type application rather than a proxy, like so:
data Foo = Foo { bar :: Int }
getField #"bar" (Foo 42) === 42
This provides compile-time extraction, although it will still need a bit of casting around to fit in with existing code in my project that manipulates standard generics-sop metadata.
This only works on single-constructor types. #dfeuer's answer also supports sum types.
Thank you #kosmikus, the coauthor of generics-sop and author of records-sop, for implementing this in response to this question!
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
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.
I have two data types and the second one is the copy of first, but with Maybe on each field.
data A = {a :: Int, b :: String}
data B = {c :: Maybe Int, d :: Maybe String}
Is there a way to make a functions
f :: A -> B
g :: B -> A -> A
without any knowledge about fields itself? (if value of first argument is nothing g will take default value from second argument)
This can be done with generics-sop, a library that extends the default Generics machinery of GHC.
"generics-sop" can take a regular record and deduce a generic representation for it. This representation has a type parameter that wraps every field, and the library allows Applicative sequence-like operations across the record fields.
{-# language TypeOperators #-}
{-# language DeriveGeneric #-}
{-# language TypeFamilies #-}
{-# language DataKinds #-}
import qualified GHC.Generics as GHC
import Generics.SOP
data A = A {a :: Int, b :: String} deriving (Show,GHC.Generic)
instance Generic A -- this Generic is from generics-sop
defaulty :: (Generic a, Code a ~ '[ xs ]) => NP Maybe xs -> a -> a
defaulty maybes r = case (from r) of
SOP (Z np) -> let result = hliftA2 (\m i -> maybe i I m) maybes np
in to (SOP (Z result))
main :: IO ()
main = do
print $ defaulty (Nothing :* Just "bar" :* Nil) (A 99 "foo")
Nothing :* Just "bar" :* Nil is a generic representation that matches the list of fields in the original record definition. Notice that each field in the representation is wrapped in Maybe.
See here for another example of generics-sop.
How about:
{-# LANGUAGE RankNTypes #-}
data R f = R { a :: f Int, b :: f String, c :: f Char }
newtype I a = I { unI :: a }
fromMaybeI :: I a -> Maybe a -> I a
fromMaybeI a Nothing = a
fromMaybeI _ (Just a) = I a
fromMaybeR :: R I -> R Maybe -> R I
fromMaybeR ri rm =
R (go a) (go b) (go c)
where
go :: (forall f. R f -> f a) -> I a
go x = fromMaybeI (x ri) (x rm)
R Maybe is the record with Maybe values, R I is the record with concrete values.
Using RankNTypes reduces the amount of boilerplate code in fromMaybeR.
One downside is that you have use I and unI to construct and
access the field values.
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.