pinch-0.1.0.0: Pinch/Internal/Value.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeOperators #-}
-- |
-- Module : Pinch.Internal.Value
-- Copyright : (c) Abhinav Gupta 2015
-- License : BSD3
--
-- Maintainer : Abhinav Gupta <mail@abhinavg.net>
-- Stability : experimental
--
-- This module defines an intermediate representation for Thrift values and
-- functions to work with the intermediate representation.
module Pinch.Internal.Value
( Value(..)
, SomeValue(..)
, castValue
, valueTType
) where
import Control.DeepSeq (NFData (..))
import Data.ByteString (ByteString)
import Data.Hashable (Hashable (..))
import Data.HashMap.Strict (HashMap)
import Data.HashSet (HashSet)
import Data.Int (Int16, Int32, Int64, Int8)
import Data.Typeable ((:~:) (..), Typeable, cast, eqT)
import Data.Vector (Vector)
import qualified Data.HashMap.Strict as M
import qualified Data.HashSet as S
import qualified Data.Vector as V
import Pinch.Internal.TType
-- | @Value@ maps directly to serialized representation of Thrift types. It
-- contains about as much information as what gets sent over the wire.
-- @Value@ objects are tagged with different 'TType' values to indicate the
-- type of the value.
--
-- Typical usage will not involve accessing the constructors for this type.
-- 'Pinch.Pinchable.Pinchable' must be used to construct 'Value' objects or
-- convert them back to original types.
data Value a where
VBool :: !Bool -> Value TBool
VByte :: !Int8 -> Value TByte
VDouble :: !Double -> Value TDouble
VInt16 :: !Int16 -> Value TInt16
VInt32 :: !Int32 -> Value TInt32
VInt64 :: !Int64 -> Value TInt64
VBinary :: !ByteString -> Value TBinary
VStruct :: !(HashMap Int16 SomeValue) -> Value TStruct
VMap :: forall k v. (IsTType k, IsTType v)
=> !(HashMap (Value k) (Value v)) -> Value TMap
VSet :: forall a. IsTType a => !(HashSet (Value a)) -> Value TSet
VList :: forall a. IsTType a => !(Vector (Value a)) -> Value TList
deriving Typeable
deriving instance Show (Value a)
instance Eq (Value a) where
VBool a == VBool b = a == b
VByte a == VByte b = a == b
VDouble a == VDouble b = a == b
VInt16 a == VInt16 b = a == b
VInt32 a == VInt32 b = a == b
VInt64 a == VInt64 b = a == b
VBinary a == VBinary b = a == b
VStruct a == VStruct b = a == b
VMap as == VMap bs = areEqual as bs
VSet as == VSet bs = areEqual as bs
VList as == VList bs = areEqual as bs
_ == _ = False
instance NFData (Value a) where
rnf (VBool a) = rnf a
rnf (VByte a) = rnf a
rnf (VDouble a) = rnf a
rnf (VInt16 a) = rnf a
rnf (VInt32 a) = rnf a
rnf (VInt64 a) = rnf a
rnf (VBinary a) = rnf a
rnf (VStruct a) = rnf a
rnf (VMap as) = rnf as
rnf (VSet as) = rnf as
rnf (VList as) = rnf as
-- | 'SomeValue' holds any value, regardless of type. This may be used when
-- the type of the value is not necessarily known at compile time. Typically,
-- this will be pattern matched on and code that depends on the value's
-- 'TType' will go inside the scope of the match.
data SomeValue where
SomeValue :: (IsTType a) => !(Value a) -> SomeValue
deriving Typeable
deriving instance Show SomeValue
instance Eq SomeValue where
SomeValue a == SomeValue b = areEqual a b
instance NFData SomeValue where
rnf (SomeValue a) = rnf a
-- | Safely attempt to cast 'SomeValue' into a 'Value'.
castValue :: Typeable a => SomeValue -> Maybe (Value a)
castValue (SomeValue v) = cast v
-- | Get the 'TType' of a 'Value'.
valueTType :: IsTType a => Value a -> TType a
valueTType _ = ttype
-- | Helper to compare two types that are not known to be equal at compile
-- time.
areEqual
:: forall a b. (Typeable a, Typeable b, Eq a) => a -> b -> Bool
areEqual x y = case eqT of
Nothing -> False
Just (Refl :: a :~: b) -> x == y
instance Hashable (Value a) where
hashWithSalt s a = case a of
VBinary x -> s `hashWithSalt` (0 :: Int) `hashWithSalt` x
VBool x -> s `hashWithSalt` (1 :: Int) `hashWithSalt` x
VByte x -> s `hashWithSalt` (2 :: Int) `hashWithSalt` x
VDouble x -> s `hashWithSalt` (3 :: Int) `hashWithSalt` x
VInt16 x -> s `hashWithSalt` (4 :: Int) `hashWithSalt` x
VInt32 x -> s `hashWithSalt` (5 :: Int) `hashWithSalt` x
VInt64 x -> s `hashWithSalt` (6 :: Int) `hashWithSalt` x
VList xs ->
V.foldr' (flip hashWithSalt) (s `hashWithSalt` (7 :: Int)) xs
VMap xs ->
M.foldrWithKey
(\k v s' -> s' `hashWithSalt` k `hashWithSalt` v)
(s `hashWithSalt` (8 :: Int))
xs
VSet xs ->
S.foldr (flip hashWithSalt) (s `hashWithSalt` (9 :: Int)) xs
VStruct fields ->
M.foldrWithKey (\k v s' -> s' `hashWithSalt` k `hashWithSalt` v)
(s `hashWithSalt` (10 :: Int))
fields
instance Hashable SomeValue where
hashWithSalt s (SomeValue v) = hashWithSalt s v