-------------------------------------------------------------------------------
-- |
-- Module : ValidLiterals
-- Copyright : (C) 2015 Merijn Verstraaten
-- License : BSD-style (see the file LICENSE)
-- Maintainer : Merijn Verstraaten <merijn@inconsistent.nl>
-- Stability : experimental
-- Portability : portable
--
-- __WARNING: For implementors of new 'Validate' instances only!__
--
-- This module exposes functions whose use is easily screwed up. Users of
-- validated literals should use the interface exported by "ValidLiterals".
-- This module only exists for implementers of new 'Validate' instances.
-------------------------------------------------------------------------------
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell #-}
module ValidLiterals.Class where
import Data.Maybe
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
-- | Class for validated, compile-time, partial conversions from type 'a' to
-- 'b'.
class Validate a b where
-- | Converts 'a' values into validated 'b' values
fromLiteral :: a -> Maybe b
-- | __WARNING: Don't use! For implementors of 'Validate' instances only!__
--
-- Produces the actual Typed Template Haskell splice for the validated
-- value to splice into the source text. Since you may want to implement a
-- 'Validate' instance for types that cannot have 'Lift' instances, this
-- function receives __both__ the original 'a' value and the resulting 'b'
-- as input.
--
-- This allows it to either:
--
-- 1. Splice the resulting 'b' into the source using it's Lift
-- instance, or a custom splice.
-- 2. Splice the initial 'a' into the source using it's Lift instance,
-- and perform the conversion again at runtime, and coercing via,
-- e.g., 'fromJust'. Since 'fromLiteral' is pure (right?!) this should
-- be perfectly safe.
--
-- Clearly, splicing the result directly is much safer (it avoids any
-- shenanigans with 'unsafePerformIO') and more efficient (no conversion at
-- runtime). However, this is just not always possible, since, for example,
-- 'ByteString' does not have a 'Lift' instance and we may still want to
-- have validated newtypes of 'ByteString'.
--
-- __Default implementation:__ The default implementation (using
-- DefaultSignatures) uses 'b''s 'Lift' instance to do the efficient thing
-- of directly splicing the resulting 'b' into the source.
spliceValid :: a -> b -> Q (TExp b)
default spliceValid :: Lift b => a -> b -> Q (TExp b)
spliceValid _ v = [|| v ||]
-- | Default implementation for 'spliceValid', uses the 'Lift' instance for 'a'
-- and 'fromJust' to redo the conversion at runtime and (unsafely) coerce from
-- 'Maybe b' to 'b'. . Since 'fromLiteral' is pure (right?!) this should be
-- perfectly safe.
hackySpliceValid :: (Lift a, Validate a b) => a -> b -> Q (TExp b)
hackySpliceValid v _ = [|| fromJust (fromLiteral v) ||]