validated-literals-0.3.0: Compile-time checking for partial smart-constructors

Copyright(C) 2015-2019 Merijn Verstraaten
LicenseBSD-style (see the file LICENSE)
MaintainerMerijn Verstraaten <merijn@inconsistent.nl>
Stabilityexperimental
Portabilityportable
Safe HaskellNone
LanguageHaskell2010

ValidLiterals

Contents

Description

To disallow invalid input it is common to define (new)types with hidden data constructors. Forcing the user to go through a smart-constructor that enforces invariants and returns Maybe ResultType, preventing the construction of data with invalid values.

However, it is also common to want to include literal values of such types in source text. Things of textual literals for HTML, HTTP, etc. In such cases smart-constructors force us to handle potential conversion failures at runtime, or abusing functions like fromJust to break away all the safety smart-constructors provide. All this despite the fact that we can statically know at compile time that the conversion will always succeed or always fails.

This package provides a typeclasses for using TH to validate the correctness of provided literals at compile. This lets you define, e.g., newtype Even = Even Integer and write:

x :: Even
x = $$(valid 38)

This will check, at compile time, that the provided Integer is, in fact, even and unwrap it from Maybe, avoiding the runtime check.

Synopsis

Documentation

class Validate a b where Source #

Class for validated, compile-time, partial conversions from type a to b.

Minimal complete definition

fromLiteralWithError | fromLiteral

Methods

fromLiteralWithError :: a -> Either String b Source #

Converts a values into validated b values, Left values are reported in the compilation error.

fromLiteral :: a -> Maybe b Source #

Converts a values into validated b values, Nothing values produce a generic error message. Use fromLiteralWithError for custom error messages.

liftResult :: Proxy a -> b -> Q (TExp b) Source #

Creates a Typed TH splice for the resulting b values, useful for avoiding the need for orphan Lift instances and allowing complex splices for types that can't be directly lifted. See the ByteString example module for an example.

liftResult :: Lift b => Proxy a -> b -> Q (TExp b) Source #

Creates a Typed TH splice for the resulting b values, useful for avoiding the need for orphan Lift instances and allowing complex splices for types that can't be directly lifted. See the ByteString example module for an example.

data ValidationFailure Source #

Exception type for failed conversions. Useful for testing and more gracefully handling compile time failures.

valid :: forall a b. Validate a b => a -> Q (TExp b) Source #

The core function of ValidLiterals, use this together with Typed Template Haskell splices to insert validated literals into your code. For example, if we assume newtype ASCII = ASCII Char where ASCII should only contain ASCII characters, we would write:

Polymorphic literals, such as numbers (or strings when OverloadedStrings is enabled) can result in ambiguous type errors with this function. Enabing the ExtendedDefaultRules extension will allow inputs to valid to be defaulted to Integer or Double allowing code to compile. A more robust solution is to use the various explicitly defaulted functions in this module, such as validInteger.

{-# LANGUAGE TemplateHaskell #-}

import ValidLiterals

x :: ASCII
x = $$(valid 'c')

validInteger :: Validate Integer b => Integer -> Q (TExp b) Source #

Integer literals lead to obnoxious defaulting complaints by GHC, by using this function you can force the defaulting to Integer, silencing these warnings.

Since Integral literals use fromInteger :: Num a => Integer -> a this function cannot cost you any precision.

validRational :: Validate Rational b => Rational -> Q (TExp b) Source #

Same as validInteger, but for Fractional values.

Since Fractional literals use fromRational :: Fractional a => Rational -> a this function cannot cost you any precision.

validString :: Validate String b => String -> Q (TExp b) Source #

Same as validInteger, but for when enabling OverloadedStrings makes String literals polymorphic.

validList :: Validate [a] b => [a] -> Q (TExp b) Source #

Same as validInteger, but for when enabling OverloadedLists makes list literals polymorphic.

Re-export from Language.Haskell.TH.Syntax

class Lift t where #

A Lift instance can have any of its values turned into a Template Haskell expression. This is needed when a value used within a Template Haskell quotation is bound outside the Oxford brackets ([| ... |]) but not at the top level. As an example:

add1 :: Int -> Q Exp
add1 x = [| x + 1 |]

Template Haskell has no way of knowing what value x will take on at splice-time, so it requires the type of x to be an instance of Lift.

A Lift instance must satisfy $(lift x) ≡ x for all x, where $(...) is a Template Haskell splice.

Lift instances can be derived automatically by use of the -XDeriveLift GHC language extension:

{-# LANGUAGE DeriveLift #-}
module Foo where

import Language.Haskell.TH.Syntax

data Bar a = Bar1 a (Bar a) | Bar2 String
  deriving Lift

Methods

lift :: t -> Q Exp #

Turn a value into a Template Haskell expression, suitable for use in a splice.

Instances
Lift Bool 
Instance details

Methods

lift :: Bool -> Q Exp #

Lift Char 
Instance details

Methods

lift :: Char -> Q Exp #

Lift Double 
Instance details

Methods

lift :: Double -> Q Exp #

Lift Float 
Instance details

Methods

lift :: Float -> Q Exp #

Lift Int 
Instance details

Methods

lift :: Int -> Q Exp #

Lift Int8 
Instance details

Methods

lift :: Int8 -> Q Exp #

Lift Int16 
Instance details

Methods

lift :: Int16 -> Q Exp #

Lift Int32 
Instance details

Methods

lift :: Int32 -> Q Exp #

Lift Int64 
Instance details

Methods

lift :: Int64 -> Q Exp #

Lift Integer 
Instance details

Methods

lift :: Integer -> Q Exp #

Lift Natural 
Instance details

Methods

lift :: Natural -> Q Exp #

Lift Word 
Instance details

Methods

lift :: Word -> Q Exp #

Lift Word8 
Instance details

Methods

lift :: Word8 -> Q Exp #

Lift Word16 
Instance details

Methods

lift :: Word16 -> Q Exp #

Lift Word32 
Instance details

Methods

lift :: Word32 -> Q Exp #

Lift Word64 
Instance details

Methods

lift :: Word64 -> Q Exp #

Lift () 
Instance details

Methods

lift :: () -> Q Exp #

Lift a => Lift [a] 
Instance details

Methods

lift :: [a] -> Q Exp #

Lift a => Lift (Maybe a) 
Instance details

Methods

lift :: Maybe a -> Q Exp #

Integral a => Lift (Ratio a) 
Instance details

Methods

lift :: Ratio a -> Q Exp #

(Lift a, Lift b) => Lift (Either a b) 
Instance details

Methods

lift :: Either a b -> Q Exp #

(Lift a, Lift b) => Lift (a, b) 
Instance details

Methods

lift :: (a, b) -> Q Exp #

(Lift a, Lift b, Lift c) => Lift (a, b, c) 
Instance details

Methods

lift :: (a, b, c) -> Q Exp #

(Lift a, Lift b, Lift c, Lift d) => Lift (a, b, c, d) 
Instance details

Methods

lift :: (a, b, c, d) -> Q Exp #

(Lift a, Lift b, Lift c, Lift d, Lift e) => Lift (a, b, c, d, e) 
Instance details

Methods

lift :: (a, b, c, d, e) -> Q Exp #

(Lift a, Lift b, Lift c, Lift d, Lift e, Lift f) => Lift (a, b, c, d, e, f) 
Instance details

Methods

lift :: (a, b, c, d, e, f) -> Q Exp #

(Lift a, Lift b, Lift c, Lift d, Lift e, Lift f, Lift g) => Lift (a, b, c, d, e, f, g) 
Instance details

Methods

lift :: (a, b, c, d, e, f, g) -> Q Exp #