HORIZON HASKELLDocslts/ghc-9.10.xc74966e2026-09-27Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · c74966e · 2026-09-27

Moduletomland-1.3.3.3Haskell2010

Toml.Codec.Generic

SPDX-License-Identifier : MPL-2.0 Maintainer : Kowainik xrom.xkov@gmail.com Stability : Stable Portability : Portable

This module contains implementation of the Generic TOML codec. If your data types are big and nested, and you want to have codecs for them without writing a lot of boilerplate code, you can find this module helpful. Below you can find the detailed explanation on how the Generic codecs work.

Consider the following Haskell data types:

data User = User
    { age     :: Int
    , address :: Address
    , socials :: [Social]
    } deriving (Generic)

data Address = Address
    { street :: Text
    , house  :: Int
    } deriving (Generic)

data Social = Social
    { name :: Text
    , link :: Text
    } deriving (Generic)

Value of the User type represents the following TOML:

age = 27

[address]
  street = "Miami Beach"
  house  = 42

[[socials]]
  name = "Twitter"
  link = "https://twitter.com/foo"

[[socials]]
  name = "GitHub"
  link = "https://github.com/bar"

Normally you would write TomlCodec for this data type like this:

userCodec :: TomlCodec User
userCodec = User
    <$> Toml.int "age" .= age
    <*> Toml.table addressCodec "address" .= address
    <*> Toml.list  socialCodec  "socials" .= socials

addressCodec :: TomlCodec Address
addressCodec = Address
    <$> Toml.text "street" .= street
    <*> Toml.int  "house"  .= house

socialCodec :: TomlCodec Social
socialCodec = Social
    <$> Toml.text "name" .= name
    <*> Toml.text "link" .= link

However, if you derive Generic instance for your data types (as we do in the example), you can write your codecs in a simpler way.

userCodec :: TomlCodec User
userCodec = genericCodec

instance HasCodec Address where
    hasCodec = Toml.table genericCodec

instance HasItemCodec Social where
    hasItemCodec = Right genericCodec

Several notes about the interface:

  1. Your top-level data types are always implemented as genericCodec (or other generic codecs).

  2. If you have a custom data type as a field of another type, you need to implement the instance of the HasCodec typeclass.

  3. If the data type appears as an element of a list, you need to implement the instance of the HasItemCodec typeclass.

  • 8 types
  • 3 classes
  • 5 values
  • Packagetomland-1.3.3.3
  • Exports16
  • LanguageHaskell2010
  • LicenceMPL-2.0
  • SourceGeneric.hs

Options

4 declarations
valuestripTypeNamePrefix :: Typeable a => Proxy a -> String -> String
#

Strips name of the type name from field name prefix.

Example6 expressions
data UserData = UserData { userDataId :: Int, userDataShortInfo :: Text }stripTypeNamePrefix (Proxy @UserData) "userDataId""id"stripTypeNamePrefix (Proxy @UserData) "userDataShortInfo""shortInfo"stripTypeNamePrefix (Proxy @UserData) "udStats""stats"stripTypeNamePrefix (Proxy @UserData) "fooBar""bar"stripTypeNamePrefix (Proxy @UserData) "name""name"

Core generic typeclass

3 declarations
classclass HasCodec a where
#

Helper typeclass for generic deriving. This instance tells how the data type should be coded if it's a field of another data type.

NOTE: If you implement TOML codecs for your data types manually, prefer more explicit Toml.int or Toml.text instead of implicit Toml.hasCodec. Implement instances of this typeclass only when using genericCodec and when your custom data types are not covered here.

Methods

Instances35HasCodec, …
classclass HasItemCodec a where
#

This typeclass tells how the data type should be coded as an item of a list. Lists in TOML can have two types: primitive and table of arrays.

Instances22HasItemCodec, …
classclass GenericCodec (f :: k -> Type) where
#

Helper class to derive TOML codecs generically.

Instances5GenericCodec

ByteString newtypes

4 declarations

There are two ways to encode ByteString in TOML:

  1. Via text.

  2. Via an array of integers (aka array of bytes).

To handle all these cases, tomland provides helpful newtypes, specifically:

As a bonus, on GHC >= 8.6 you can use these newtypes with the DerivingVia extensions for your own ByteString types.

newtype MyByteString = MyByteString
    { unMyByteString :: ByteString
    } deriving HasCodec via ByteStringAsBytes
newtypenewtype ByteStringAsBytes
#

Newtype wrapper over ByteString to be used for array of integers representation.

Instances4Eq, Show, HasCodec, HasItemCodec
newtypenewtype LByteStringAsBytes
#

Newtype wrapper over lazy ByteString to be used for array of integers representation.

Instances4Eq, Show, HasCodec, HasItemCodec

Deriving Via

2 declarations
newtypenewtype TomlTable a
#

newtype for generic deriving of HasCodec typeclass for custom data types that should we wrapped into separate table. Use it only for data types that are fields of another data types.

data Person = Person
    { personName    :: !Text
    , personAddress :: !Address
    } deriving (Generic)

data Address = Address
    { addressStreet :: !Text
    , addressHouse  :: !Int
    } deriving (Generic)
      deriving HasCodec via TomlTable Address

personCodec :: TomlCodec Person
personCodec = stripTypeNameCodec

personCodec corresponds to the TOML of the following structure:

name = "foo"
[address]
    addressStreet = "Bar"
    addressHouse = 42

Constructors

Instances2HasCodec, HasItemCodec
newtypenewtype TomlTableStrip a
#

newtype for generic deriving of HasCodec typeclass for custom data types that should be wrapped into a separate table.

Similar to TomlTable but also strips the data type name prefix from TOML keys.

personCodec from the TomlTable comment corresponds to the TOML of the following structure:

name = "foo"
[address]
    street = "Bar"
    house = 42
Instances2HasCodec, HasItemCodec