{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE CPP #-} #if __GLASGOW_HASKELL__ >= 710 {-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-} #endif -- | -- Copyright: (C) 2013 Amgen, Inc. -- -- Definition of 'SEXPTYPE', which classifies the possible forms of an -- R expression (a 'SEXP'). It is normally not necessary to import this module -- directly, since it is reexported by "Foreign.R". -- -- This is done in a separate module because we want to use hsc2hs rather than -- c2hs for discharging the boilerplate around 'SEXPTYPE'. This is because -- 'SEXPTYPE' is nearly but not quite a true enumeration and c2hs has trouble -- dealing with that. -- -- This module also defines a singleton version of 'SEXPTYPE', called -- 'SSEXPTYPE'. This is actually a family of types, one for each possible -- 'SEXPTYPE'. Singleton types are a way of emulating dependent types in -- a language that does not have true dependent type. They are useful in -- functions whose result type depends on the value of one of its arguments. See -- e.g. 'Foreign.R.allocVector'. module Foreign.R.Type where #include import Foreign.R.Constraints import Internal.Error import qualified Language.Haskell.TH.Syntax as Hs import qualified Language.Haskell.TH.Lib as Hs import Data.Singletons.TH import Control.DeepSeq (NFData(..)) import Foreign (castPtr) import Foreign.C (CInt) import Foreign.Storable(Storable(..)) import Prelude hiding (Bool(..)) -- | R \"type\". Note that what R calls a \"type\" is not what is usually meant -- by the term: there is really only a single type, called 'SEXP', and an -- R "type" in fact refers to the /class/ or /form/ of the expression. -- -- To better illustrate the distinction, note that any sane type system normally -- has the /subject reduction property/: that the type of an expression is -- invariant under reduction. For example, @(\x -> x) 1@ has type 'Int', and so -- does the value of this expression, @2@, have type 'Int'. Yet the /form/ of -- the expression is an application of a function to a literal, while the form -- of its reduct is an integer literal. -- -- We introduce convenient Haskell-like names for forms because this datatype is -- used to index 'SEXP' and other types through the @DataKinds@ extension. -- data SEXPTYPE = Nil | Symbol | List | Closure | Env | Promise | Lang | Special | Builtin | Char | Logical | Int | Real | Complex | String | DotDotDot | Any | Vector | Expr | Bytecode | ExtPtr | WeakRef | Raw | S4 | New | Free | Fun deriving (Eq, Show) instance Enum SEXPTYPE where fromEnum Nil = #const NILSXP fromEnum Symbol = #const SYMSXP fromEnum List = #const LISTSXP fromEnum Closure = #const CLOSXP fromEnum Env = #const ENVSXP fromEnum Promise = #const PROMSXP fromEnum Lang = #const LANGSXP fromEnum Special = #const SPECIALSXP fromEnum Builtin = #const BUILTINSXP fromEnum Char = #const CHARSXP fromEnum Logical = #const LGLSXP fromEnum Int = #const INTSXP fromEnum Real = #const REALSXP fromEnum Complex = #const CPLXSXP fromEnum String = #const STRSXP fromEnum DotDotDot = #const DOTSXP fromEnum Any = #const ANYSXP fromEnum Vector = #const VECSXP fromEnum Expr = #const EXPRSXP fromEnum Bytecode = #const BCODESXP fromEnum ExtPtr = #const EXTPTRSXP fromEnum WeakRef = #const WEAKREFSXP fromEnum Raw = #const RAWSXP fromEnum S4 = #const S4SXP fromEnum New = #const NEWSXP fromEnum Free = #const FREESXP fromEnum Fun = #const FUNSXP toEnum (#const NILSXP) = Nil toEnum (#const SYMSXP) = Symbol toEnum (#const LISTSXP) = List toEnum (#const CLOSXP) = Closure toEnum (#const ENVSXP) = Env toEnum (#const PROMSXP) = Promise toEnum (#const LANGSXP) = Lang toEnum (#const SPECIALSXP) = Special toEnum (#const BUILTINSXP) = Builtin toEnum (#const CHARSXP) = Char toEnum (#const LGLSXP) = Logical toEnum (#const INTSXP) = Int toEnum (#const REALSXP) = Real toEnum (#const CPLXSXP) = Complex toEnum (#const STRSXP) = String toEnum (#const DOTSXP) = DotDotDot toEnum (#const ANYSXP) = Any toEnum (#const VECSXP) = Vector toEnum (#const EXPRSXP) = Expr toEnum (#const BCODESXP) = Bytecode toEnum (#const EXTPTRSXP) = ExtPtr toEnum (#const WEAKREFSXP) = WeakRef toEnum (#const RAWSXP) = Raw toEnum (#const S4SXP) = S4 toEnum (#const NEWSXP) = New toEnum (#const FREESXP) = Free toEnum (#const FUNSXP) = Fun toEnum _ = violation "toEnum" "Unknown R type." instance NFData SEXPTYPE where rnf = (`seq` ()) genSingletons [''SEXPTYPE] instance Hs.Lift SEXPTYPE where lift a = [| $(Hs.conE (Hs.mkName $ "Foreign.R.Type." ++ show a)) |] -- | R uses three-valued logic. data Logical = False | True | NA -- XXX no Enum instance because NA = INT_MIN, not representable as an Int on -- 32-bit systems. deriving (Eq, Show) instance Storable Logical where sizeOf _ = sizeOf (undefined :: CInt) alignment _ = alignment (undefined :: CInt) poke ptr False = poke (castPtr ptr) (0 :: CInt) poke ptr True = poke (castPtr ptr) (1 :: CInt) -- Currently NA_LOGICAL = INT_MIN. poke ptr NA = poke (castPtr ptr) (#{const INT_MIN} :: CInt) peek ptr = do x <- peek (castPtr ptr) case x :: CInt of 0 -> return False 1 -> return True #{const INT_MIN} -> return NA _ -> failure "Storable Logical peek" "Not a Logical." -- | Used where the R documentation speaks of "pairlists", which are really just -- regular lists. type PairList = List -- Use a macro to avoid having to define append at the type level. #let VECTOR_FORMS = " 'Char \ ': 'Logical \ ': 'Int \ ': 'Real \ ': 'Complex \ ': 'String \ ': 'Vector \ ': 'Expr \ ': 'WeakRef \ ': 'Raw" -- | Constraint synonym grouping all vector forms into one class. @IsVector a@ -- holds iff R's @is.vector()@ returns @TRUE@. type IsVector (a :: SEXPTYPE) = (SingI a, a :∈ #{VECTOR_FORMS} ': '[]) -- | Non-atomic vector forms. See @src\/main\/memory.c:SET_VECTOR_ELT@ in the -- R source distribution. type IsGenericVector (a :: SEXPTYPE) = (SingI a, a :∈ [Vector, Expr, WeakRef]) -- | @IsList a@ holds iff R's @is.list()@ returns @TRUE@. type IsList (a :: SEXPTYPE) = (SingI a, a :∈ #{VECTOR_FORMS} ': List ': '[]) -- | @IsPairList a@ holds iff R's @is.pairlist()@ returns @TRUE@. type IsPairList (a :: SEXPTYPE) = (SingI a, a :∈ [List, Nil])