Skip to content

Commit f2f8ee1

Browse files
authored
Add newtype support (#16)
* Add newtype support * Print unexpected declarations in the error instead of just "not a datatype" * Add flag for newtype behaviour (silently transform to datatype, transform with warning, error)
1 parent f49d0fe commit f2f8ee1

File tree

1 file changed

+35
-11
lines changed

1 file changed

+35
-11
lines changed

src/Extensible.hs

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ module Extensible
272272
-- ** Template Haskell re-exports
273273
newName, varT,
274274
-- * Generating extensible datatypes
275-
extensible, extensibleWith, Config (..), defaultConfig)
275+
extensible, extensibleWith, Config (..), defaultConfig, WarningType (..))
276276
where
277277

278278
import Language.Haskell.TH as TH
@@ -337,8 +337,10 @@ qualifyWith m n = case nameModule n of
337337
Just _ -> n
338338

339339

340-
-- | Configuration options for how to name the generated constructors, type
341-
-- families, etc.
340+
data WarningType = Ignore | Warn | Error deriving (Eq, Show, Lift)
341+
342+
-- | Configuration options for warning behaviour, as well as how to name the
343+
-- generated constructors, type families, etc.
342344
data Config = Config {
343345
-- | Applied to input datatype's name to get extensible type's name
344346
datatypeName :: NameAffix,
@@ -366,7 +368,10 @@ data Config = Config {
366368
defExtRecName :: NameAffix,
367369
-- | Applied to datatype name to get the name of the extension
368370
-- generator function
369-
extFunName :: NameAffix
371+
extFunName :: NameAffix,
372+
-- | What to do when encountering a newtype. For @Warn@ and @Ignore@, it is
373+
-- treated as a strict datatype.
374+
newtypeWarn :: WarningType
370375
} deriving (Eq, Show, Lift)
371376

372377
-- | Default config:
@@ -375,15 +380,16 @@ data Config = Config {
375380
-- Config {
376381
-- datatypeName = NameSuffix \"'\",
377382
-- constructorName = NameSuffix \"'\",
378-
-- bundleName = NameSuffix "All",
383+
-- bundleName = NameSuffix \"All\",
379384
-- annotationName = NamePrefix \"X\",
380385
-- extensionName = NameSuffix \"X\",
381386
-- extensionLabel = NamePrefix \"ext\",
382387
-- extRecordName = NamePrefix \"Ext\",
383388
-- extRecTypeName = NamePrefix \"type\",
384389
-- extRecNameName = NamePrefix \"name\",
385390
-- defExtRecName = NamePrefix \"default\",
386-
-- extFunName = NamePrefix \"extend\"
391+
-- extFunName = NamePrefix \"extend\",
392+
-- newtypeWarn = Warn
387393
-- }
388394
-- @
389395
defaultConfig :: Config
@@ -398,7 +404,8 @@ defaultConfig = Config {
398404
extRecTypeName = NamePrefix "type",
399405
extRecNameName = NamePrefix "name",
400406
defExtRecName = NamePrefix "default",
401-
extFunName = NamePrefix "extend"
407+
extFunName = NamePrefix "extend",
408+
newtypeWarn = Warn
402409
}
403410

404411

@@ -433,15 +440,32 @@ data SimpleDeriv =
433440

434441
-- | Extract a 'SimpleData' from a 'Dec', if it is a datatype with the given
435442
-- restrictions.
436-
simpleData :: Dec -> Q SimpleData
437-
simpleData (DataD ctx name tvs kind cons derivs)
443+
simpleData :: WarningType -> Dec -> Q SimpleData
444+
simpleData _w (DataD ctx name tvs kind cons derivs)
438445
| not $ null ctx = fail "data contexts unsupported"
439446
| Just _ <- kind = fail "kind signatures unsupported"
440447
| otherwise =
441448
SimpleData name tvs
442449
<$> traverse simpleCon cons
443450
<*> traverse simpleDeriv derivs
444-
simpleData _ = fail "not a datatype"
451+
simpleData Error (NewtypeD _ name _ _ _ _) =
452+
fail $
453+
"newtype " ++ nameBase name ++ " found\n" ++
454+
"please replace it with a datatype"
455+
simpleData Warn n@(NewtypeD _ name _ _ _ _) = do
456+
reportWarning $
457+
"replacing newtype " ++ nameBase name ++ " with data\n" ++
458+
"(due to adding another field and a second constructor)\n" ++
459+
"you may want to replace the newtype with a (strict) datatype"
460+
simpleData Ignore n
461+
simpleData Ignore (NewtypeD ctx name tvs kind con derivs) =
462+
simpleData Ignore $ DataD ctx name tvs kind [makeStrict con] derivs
463+
where
464+
makeStrict = everywhere $ mkT $ const $ Bang NoSourceUnpackedness SourceStrict
465+
simpleData _w d =
466+
fail $
467+
"only datatype declarations are supported inside extensible; found\n" ++
468+
pprint d
445469

446470
-- | Extract a 'SimpleCon' from a 'Con', if it is the 'NormalC' case.
447471
simpleCon :: Con -> Q SimpleCon
@@ -468,7 +492,7 @@ extensible = extensibleWith defaultConfig
468492
-- function spits out.
469493
extensibleWith :: Config -> DecsQ -> DecsQ
470494
extensibleWith conf ds = do
471-
ds' <- traverse simpleData =<< ds
495+
ds' <- traverse (simpleData (newtypeWarn conf)) =<< ds
472496
home <- loc_module <$> location
473497
makeExtensible conf home ds'
474498

0 commit comments

Comments
 (0)