partial-records: Template haskell utilities for constructing records with default values

[ bsd3, data, library ] [ Propose Tags ]

If you have a datatype with a lot of default-able fields, e.g.

data Foo =
  { fld1 :: Maybe Int
  , fld2 :: Maybe Char
  , fld3 :: Word
  }

and you want to avoid the the boilerplate of writing all the default values every time you construct a record of this type, you could write a "default value" of this type:

defaultFoo :: Foo
defaultFoo = Foo { fld1 = Nothing, fld2 = Nothing, fld3 = 0 }

You could then use record modification syntax to make necessary changes to this value. But perhaps you can't / don't want to provide default values for all of the fields, but only some of them? You could implement a "default smart constructor" that would take the non-optional arguments and then fill in the optional ones like so:

defaultFoo :: Word -> Foo
defaultFoo x = Foo { fld1 = Nothing, fld2 = Nothing, fld3 = x }

But then you lose the benefit of record syntax: you can't name the fields you're providing values for.

This package reconciles the two problems: with only a little bit of Template Haskell it provides a way to construct a record with optional fields while also letting you refer to the names of those fields. You make two splices:

mkToPartial ''Foo
  -- defines 'mkfld1', 'mkfld2', 'mkfld3'
mkFromPartial "mkFoo" [t|Foo|] [|Foo { fld1 = Nothing, fld2 = Nothing }
  |]
  -- defines 'mkFoo'

And then you can use them like so:

val :: Foo
val = mkFoo
  $ mkfld3 123
  ? mkfld1 (Just 456)
-- val = Foo { fld1 = Just 456, fld2 = Nothing, fld3 = 123 }

The Template Haskell splice lets you define default values for a subset of the fields, and those defaults will be used when you call mkFoo. You can list fields in any order, but if you omit a mandatory field (one that doesn't have a default), that would be a type error at compile time.

You can make multiple Data.Partial.TH.mkFromPartial splices, this is occasionally useful for parameterized types, for example:

data Bar a =
  { bar1 :: Maybe Int
  , bar2 :: a
  }
mkToPartial ''Bar
mkFromPartial "mkBar" [t|forall a. Bar a|]
  [|Bar { bar1 = Nothing }
  |]
  -- mkBar :: ... -> Bar a, and bar2 is a required field
mkFromPartial "mkBarMaybe" [t|forall a. Bar (Maybe a)|]
  [|Bar { bar1 = Nothing, bar2 = Nothing }
  |]
  -- mkBarMaybe :: ... -> Bar (Maybe a), and bar2 is an optional field

Downloads

Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

Versions [RSS] 0.1.0.0, 0.2.0.0, 0.2.1.0, 0.2.2.0, 0.2.2.1
Dependencies base (>=4.9 && <=4.13), tagged (>=0.8), template-haskell (>=2.11), transformers (>=0.5) [details]
License BSD-3-Clause
Copyright (C) mniip 2019
Author mniip
Maintainer mniip@mniip.com
Revised Revision 1 made by mniip at 2019-10-15T04:30:30Z
Category Data
Home page https://github.com/mniip/partial-records
Uploaded by mniip at 2019-10-15T04:23:58Z
Distributions
Downloads 1743 total (14 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2019-10-15 [all 1 reports]