All Projects → alonsodomin → haskell-schema

alonsodomin / haskell-schema

Licence: LGPL-3.0 License
A library for describing Haskell data types and obtain free generators, JSON codecs, pretty printers, etc.

Programming Languages

haskell
3896 projects
shell
77523 projects

Projects that are alternatives of or similar to haskell-schema

ty
Here is a schema checker which can return well typed results. Tell your friends!
Stars: ✭ 21 (+31.25%)
Mutual labels:  schema
coffeediz.schema
Набор компонентов 1С-Битрикс для реализации микроразметки по схеме Schema.org
Stars: ✭ 14 (-12.5%)
Mutual labels:  schema
linkedin-to-jsonresume
Browser extension to turn a LinkedIn profile page into a JSON Resume export.
Stars: ✭ 93 (+481.25%)
Mutual labels:  schema
metaschema
Schema definition and validation 💡
Stars: ✭ 25 (+56.25%)
Mutual labels:  schema
schema-shot
Framework-agnostic snapshot testing using "schema by example" for highly dynamic data
Stars: ✭ 34 (+112.5%)
Mutual labels:  schema
gbXML Schemas
Includes current and previous versions of the Green Building XML (gbXML) schema
Stars: ✭ 22 (+37.5%)
Mutual labels:  schema
stateless
A library which is not recognized as citizen of any infrastructure.
Stars: ✭ 26 (+62.5%)
Mutual labels:  higher-order
Clockwork
A roleplaying framework developed by Cloud Sixteen for the people.
Stars: ✭ 37 (+131.25%)
Mutual labels:  schema
ckanext-scheming
Easy, shareable custom CKAN schemas
Stars: ✭ 67 (+318.75%)
Mutual labels:  schema
ecto generator
Generate Ecto schemas from existing database in Phoenix - Elixir
Stars: ✭ 20 (+25%)
Mutual labels:  schema
superstruct
Rust library for versioned data types
Stars: ✭ 27 (+68.75%)
Mutual labels:  schema
edd
Erlang Declarative Debugger
Stars: ✭ 20 (+25%)
Mutual labels:  quickcheck
tyshemo
A javascript runtime data type checking system and morden reactive state management model.
Stars: ✭ 70 (+337.5%)
Mutual labels:  schema
micro-compose
Higher-order "compose" function
Stars: ✭ 22 (+37.5%)
Mutual labels:  higher-order
thema
A CUE-based framework for portable, evolvable schema
Stars: ✭ 41 (+156.25%)
Mutual labels:  schema
php-schema.org-mapping
A fluent interface to create mappings using Schema.org for Microdata and JSON-LD.
Stars: ✭ 31 (+93.75%)
Mutual labels:  schema
normalize-pkg
Normalize values in package.json to improve compatibility, programmatic readability and usefulness with third party libs.
Stars: ✭ 18 (+12.5%)
Mutual labels:  schema
craftql
A CLI tool to visualize GraphQL schemas and to output a graph data structure as a graphviz .dot format
Stars: ✭ 75 (+368.75%)
Mutual labels:  schema
flycouchdb
Migration tool for CouchDB
Stars: ✭ 20 (+25%)
Mutual labels:  schema
openapi-schema-validator
OpenAPI schema validator for Python
Stars: ✭ 35 (+118.75%)
Mutual labels:  schema

Haskell Schema

Build Status License: LGPL v3 Hackage

Haskell Schema (or hschema) is a library with the purpose of describing data (or domains) and use that information to automatically derive serialization codecs (JSON, binary, etc.), arbitrary generators, pretty printers and much more. It is heavily inspired by the Scala library xenomorph (in fact, it is a port of the same ideas), which was introduced in the following talk at Scala World 2017:

Describing Data...with free applicative functors (and more)—Kris Nuttycombe

Motivation

The idea behind it is that, given a domain model you want to work with, you can use this library to build a description of it (or schema) that is totally independent of the actual code representation of the given domain model. After that, you can leverage the mechanics behind this library to generate QuickCheck generators, JSON parsers, binary codecs, etc.

Isn't that much work? What about deriving Generic?

Deriving Generic from your data and deriving your encoders from there seems pretty reasonable, and it's usually very concise, isn't it? But there is a problem with that, usually the data that you are going to be serializing over the wire (that's why you need your JSON, binary, etc. codecs) forms part of your public protocol. That means that every time you modify one of those data items, you are in danger of breaking your compatibility.

On top of that, what about supporting two versions of your protocol? That will get hairy quite quickly. By defining the schema separated from the actual data types, you can evolve your domain model without modifying the actual schema, add a new schema version and even define migrations between them.

How to use it?

Haskell Schema is distributed as a set of packages that together provide a cohesive set of features:

  • hschema: This is the core package, defining the base building pieces
  • hschema-aeson: This is a package that provides JSON encoding and decoding using Aeson.
  • hschema-quickcheck: This package will provide with QuickCheck generators based on our schema.
  • hschema-prettyprinter: This package brings pretty priting utilities.

In the following example we are going to make use of all those packages.

Example

Let's start by defining a some data types alongside some lenses:

{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies      #-}

import Control.Lens
import Data.Time (UTCTime)

data Role =
    UserRole UserRole
  | AdminRole AdminRole
  deriving (Eq, Show)

data UserRole = UserRole'
  deriving (Eq, Show)

data AdminRole = AdminRole' { department :: String, subordinateCount :: Int }
  deriving (Eq, Show)

_UserRole :: Prism' Role UserRole
_UserRole = prism' UserRole $ \case
    UserRole x -> Just x
    _          -> Nothing

_AdminRole :: Prism' Role AdminRole
_AdminRole = prism' AdminRole $ \case
    AdminRole x -> Just x
    _           -> Nothing

data Person = Person { personName :: String, birthDate :: Maybe UTCTime, roles :: [Role] }
  deriving (Eq, Show)

Now, defining the schema for the Person data type, you define each of the fields individually (name, type and getter) and combine them using an applicative:

import           Data.Convertible
import qualified Data.Schema             as S
import           Data.Schema.JSON
import qualified Data.Schema.JSON.Simple as JSON

utcTimeSchema :: JsonSchema UTCTime
utcTimeSchema = S.alias (iso convert convert) (JSON.int :: JsonSchema Integer)

personSchema :: JsonSchema Person
personSchema = S.record
             ( Person
             <$> S.field    "name"      JSON.string         (to personName)
             <*> S.optional "birthDate" utcTimeSchema       (to birthDate)
             <*> S.field    "roles"     (S.list roleSchema) (to roles)
             )

The schema for the Role data type is defined as a list of alternatives alongside a prism as an accessor:

adminRole :: JsonSchema AdminRole
adminRole = S.record
          ( AdminRole'
          <$> S.field "department"       JSON.string (to department)
          <*> S.field "subordinateCount" JSON.int    (to subordinateCount)
          )

roleSchema :: JsonSchema Role
roleSchema = S.oneOf
           [ S.alt "user"  (S.const UserRole') _UserRole
           , S.alt "admin" adminRole           _AdminRole
           ]

Once you have defined the schema, by proving an instance for the HasSchema typeclass, you'll get JSON decoders, encoders, generators, etc. for free right away.

import Data.Schema (HasSchema(..))

instance HasSchema Person where
  type PrimitivesOf Person = JsonType

  getSchema = personSchema

Pretty Printer

There is also built-in support for pretty printing schemas:

import Data.Schema.PrettyPrint

putSchema personSchema

That will produce an output similar to the following:

* roles :: [
  - user
  - admin
    * subordinateCount :: Number
    * department :: Text
]
* birthDate ?:: Number
* name :: Text

Not happy with that? What about a pretty printer based on the given schema? Just use the prettyPrinter function, which will return you a a -> IO () function that you can use to print your data types:

pprintPerson :: Person -> IO ()
pprintPerson = prettyPrinter personSchema

Credits

All thanks to Kris Nuttycombe for his excellent work in xenomorph, this project would be have been impossible without his work.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].