Skip to content

Commit e2d2d35

Browse files
Add specs for Language.R*
1 parent f7a6951 commit e2d2d35

File tree

5 files changed

+95
-67
lines changed

5 files changed

+95
-67
lines changed

inline-r/inline-r.cabal

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,19 @@ library
5959
Foreign.R.Internal
6060
Foreign.R.Parse
6161
Foreign.R.Type
62+
Foreign.R.Type.Singletons
6263
-- H.Prelude
6364
-- H.Prelude.Interactive
64-
-- Language.R
65+
Language.R
6566
-- Language.R.Debug
6667
Language.R.GC
6768
Language.R.Globals
6869
Language.R.HExp
6970
Language.R.Instance
70-
-- Language.R.Internal
71+
Language.R.Internal
7172
Language.R.Internal.FunWrappers
7273
Language.R.Internal.FunWrappers.TH
73-
-- Language.R.Literal
74+
Language.R.Literal
7475
-- Language.R.Matcher
7576
-- Language.R.QQ
7677
if !os(windows)

inline-r/src/Language/R.hs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,8 @@ import qualified Data.Vector.SEXP as Vector
3636
import Control.Monad.R.Class
3737
import Foreign.R
3838
( SEXP
39-
, SomeSEXP(..)
4039
, typeOf
41-
, asTypeOf
4240
, cast
43-
, unSomeSEXP
44-
, unsafeCoerce
4541
)
4642
import qualified Foreign.R as R
4743
import qualified Foreign.R.Parse as R
@@ -73,7 +69,7 @@ import Prelude
7369
-- the dependency hierarchy.
7470

7571
-- | Parse and then evaluate expression.
76-
parseEval :: ByteString -> IO (SomeSEXP V)
72+
parseEval :: ByteString -> IO (SEXP V)
7773
parseEval txt = useAsCString txt $ \ctxt ->
7874
R.withProtected (R.mkString ctxt) $ \rtxt ->
7975
alloca $ \status -> do
@@ -90,18 +86,20 @@ parseEval txt = useAsCString txt $ \ctxt ->
9086
--
9187
-- This function uses continuation because this is an easy way to make
9288
-- operations GC-safe.
93-
parseFile :: FilePath -> (SEXP s 'R.Expr -> IO a) -> IO a
89+
{-@ parseFile :: FilePath -> (SEXP s 'R.Expr -> IO a) -> IO a @-}
90+
parseFile :: FilePath -> (SEXP s -> IO a) -> IO a
9491
{-# DEPRECATED parseFile "Use [r| parse(file=\"path/to/file\") |] instead." #-}
9592
parseFile fl f = do
9693
withCString fl $ \cfl ->
9794
R.withProtected (R.mkString cfl) $ \rfl ->
9895
r1 (C8.pack "parse") rfl >>= \(R.SomeSEXP s) ->
99-
return (R.unsafeCoerce s) `R.withProtected` f
96+
return s `R.withProtected` f
10097

98+
{-@ parseText :: String -> Bool -> IO (R.SEXP V 'R.Expr) @-}
10199
parseText
102100
:: String -- ^ Text to parse
103101
-> Bool -- ^ Whether to annotate the AST with source locations.
104-
-> IO (R.SEXP V 'R.Expr)
102+
-> IO (R.SEXP V)
105103
{-# DEPRECATED parseText "Use [r| parse(text=...) |] instead." #-}
106104
parseText txt b = do
107105
s <- parseEval $ C8.pack $
@@ -112,22 +110,26 @@ parseText txt b = do
112110
| otherwise = "FALSE"
113111

114112
-- | Internalize a symbol name.
115-
install :: MonadR m => String -> m (SEXP V 'R.Symbol)
113+
{-@ install :: String -> m (SEXP V 'R.Symbol) @-}
114+
install :: MonadR m => String -> m (SEXP V)
116115
install = io . installIO
117116

118117
{-# DEPRECATED string, strings "Use mkSEXP instead" #-}
119118

120119
-- | Create an R character string from a Haskell string.
121-
string :: String -> IO (SEXP V 'R.Char)
120+
{-@ string :: String -> IO (SEXP V 'R.Char) @-}
121+
string :: String -> IO (SEXP V)
122122
string str = withCString str R.mkChar
123123

124124
-- | Create an R string vector from a Haskell string.
125-
strings :: String -> IO (SEXP V 'R.String)
125+
{-@ strings :: String -> IO (SEXP V 'R.String) @-}
126+
strings :: String -> IO (SEXP V)
126127
strings str = withCString str R.mkString
127128

128129
-- | Evaluate a (sequence of) expression(s) in the given environment, returning the
129130
-- value of the last.
130-
evalEnv :: MonadR m => SEXP s a -> SEXP s 'R.Env -> m (SomeSEXP (Region m))
131+
{-@ evalEnv :: SEXP s a -> SEXP s 'R.Env -> m (SEXP (Region m)) @-}
132+
evalEnv :: MonadR m => SEXP s -> SEXP s -> m (SEXP (Region m))
131133
evalEnv (hexp -> Language.R.HExp.Expr _ v) rho = acquireSome =<< do
132134
io $ alloca $ \p -> do
133135
mapM_ (\(SomeSEXP s) -> void $ R.protect s) (Vector.toList v)
@@ -146,14 +148,15 @@ evalEnv x rho = acquireSome =<< do
146148
return v
147149

148150
-- | Evaluate a (sequence of) expression(s) in the global environment.
149-
eval :: MonadR m => SEXP s a -> m (SomeSEXP (Region m))
151+
eval :: MonadR m => SEXP s -> m (SEXP (Region m))
150152
eval x = evalEnv x (R.release globalEnv)
151153

152154
-- | Silent version of 'eval' function that discards it's result.
153-
eval_ :: MonadR m => SEXP s a -> m ()
155+
eval_ :: MonadR m => SEXP s -> m ()
154156
eval_ = void . eval
155157

156158
-- | Throw an R error as an exception.
159+
{-@ throwR :: R.SEXP s 'R.Env -> m a @-}
157160
throwR :: MonadR m => R.SEXP s 'R.Env -- ^ Environment in which to find error.
158161
-> m a
159162
throwR env = getErrorMessage env >>= io . throwIO . R.RError
@@ -173,7 +176,8 @@ throwRMessage :: MonadR m => String -> m a
173176
throwRMessage = io . throwIO . R.RError
174177

175178
-- | Read last error message.
176-
getErrorMessage :: MonadR m => R.SEXP s 'R.Env -> m String
179+
{-@ getErrorMessage :: MonadR m => R.SEXP s 'R.Env -> m String @-}
180+
getErrorMessage :: MonadR m => R.SEXP s -> m String
177181
getErrorMessage e = io $ do
178182
R.withProtected (withCString "geterrmessage" ((R.install >=> R.lang1))) $ \f -> do
179183
R.withProtected (return (R.release e)) $ \env -> do

inline-r/src/Language/R/Internal.hs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,18 @@ inVoid = id
1919
{-# INLINE inVoid #-}
2020

2121
-- | Call a pure unary R function of the given name in the global environment.
22-
r1 :: ByteString -> SEXP s a -> IO (SomeSEXP V)
22+
r1 :: ByteString -> SEXP s -> IO (SEXP V)
2323
r1 fn a =
2424
useAsCString fn $ \cfn -> R.install cfn >>= \f ->
2525
R.withProtected (R.lang2 f (R.release a)) (unsafeRunRegion . inVoid . eval)
2626

2727
-- | Call a pure binary R function. See 'r1' for additional comments.
28-
r2 :: ByteString -> SEXP s a -> SEXP s b -> IO (SomeSEXP V)
28+
r2 :: ByteString -> SEXP s -> SEXP s -> IO (SEXP V)
2929
r2 fn a b =
3030
useAsCString fn $ \cfn -> R.install cfn >>= \f ->
3131
R.withProtected (R.lang3 f (R.release a) (R.release b)) (unsafeRunRegion . inVoid . eval)
3232

3333
-- | Internalize a symbol name.
34-
installIO :: String -> IO (SEXP V 'R.Symbol)
34+
{-@ installIO :: String -> IO (TSEXP V Foreign.R.Type.Symbol) @-}
35+
installIO :: String -> IO (SEXP V)
3536
installIO str = withCString str R.install

inline-r/src/Language/R/Internal.hs-boot

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ module Language.R.Internal where
44

55
import Control.Memory.Region
66
import Data.ByteString (ByteString)
7-
import Foreign.R (SEXP, SomeSEXP(..))
8-
import qualified Foreign.R.Type as R
7+
import Foreign.R (SEXP)
98

10-
r1 :: ByteString -> SEXP s a -> IO (SomeSEXP V)
11-
r2 :: ByteString -> SEXP s a -> SEXP s b -> IO (SomeSEXP V)
12-
installIO :: String -> IO (SEXP V 'R.Symbol)
9+
r1 :: ByteString -> SEXP s -> IO (SEXP V)
10+
r2 :: ByteString -> SEXP s -> SEXP s -> IO (SEXP V)
11+
installIO :: String -> IO (SEXP V)

inline-r/src/Language/R/Literal.hs

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
{-# Language FlexibleInstances #-}
1010
{-# Language FunctionalDependencies #-}
1111
{-# Language GADTs #-}
12+
{-# Language KindSignatures #-}
1213
{-# Language LambdaCase #-}
1314
{-# LANGUAGE OverloadedStrings #-}
1415
{-# LANGUAGE ScopedTypeVariables #-}
@@ -17,8 +18,11 @@
1718
{-# Language ViewPatterns #-}
1819

1920
-- required to not warn about IsVector usage.
20-
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
21+
{-# OPTIONS_GHC -fno-warn-redundant-constraints -fplugin-opt=LiquidHaskell:--skip-module=False #-}
22+
{-@ LIQUID "--exact-data-cons" @-} -- needed to have LH accept specs in module HExp
23+
{-@ LIQUID "--prune-unsorted" @-}
2124
module Language.R.Literal
25+
{-
2226
( -- * Literals conversion
2327
Literal(..)
2428
, toPairList
@@ -33,16 +37,20 @@ module Language.R.Literal
3337
, mkProtectedSEXPVectorIO
3438
-- * Internal
3539
, funToSEXP
36-
) where
40+
) -} where
3741

3842
import Control.Memory.Region
3943
import Control.Monad.R.Class
4044
import qualified Data.Vector.SEXP as SVector
4145
import qualified Data.Vector.SEXP.Mutable as SMVector
46+
import qualified Data.Vector.SEXP.Mutable as Mutable -- Needed to help LH name resolution
47+
import Foreign.C -- Needed to help LH name resolution
4248
import qualified Foreign.R as R
43-
import qualified Foreign.R.Internal as R (somesexp)
44-
import Foreign.R.Type ( IsVector, SSEXPTYPE )
45-
import Foreign.R ( SEXP, SomeSEXP(..) )
49+
import Foreign.R.Type ( IsVector )
50+
import Foreign.R.Type.Singletons (SSEXPTYPE)
51+
import Foreign.R ( SEXP )
52+
import GHC.ForeignPtr -- Needed to help LH name resolution
53+
import GHC.ST -- Needed to help LH name resolution
4654
import Internal.Error
4755
import {-# SOURCE #-} Language.R.Internal (r1)
4856
import Language.R.Globals (nilValue)
@@ -67,84 +75,98 @@ import qualified GHC.Foreign as GHC
6775
import GHC.IO.Encoding.UTF8
6876
import System.IO.Unsafe ( unsafePerformIO )
6977

78+
{-@ measure Language.R.Literal.literalRType :: a -> R.SEXPTYPE @-}
79+
7080
-- | Values that can be converted to 'SEXP'.
71-
class SingI ty => Literal a ty | a -> ty where
81+
class SingI ty => Literal a (ty :: R.SEXPTYPE) | a -> ty where
7282
-- | Internal function for converting a literal to a 'SEXP' value. You
7383
-- probably want to be using 'mkSEXP' instead.
74-
mkSEXPIO :: a -> IO (SEXP V ty)
75-
fromSEXP :: SEXP s ty -> a
84+
{-@ mkSEXPIO :: x:a -> IO (TSEXP V (literalRType x)) @-}
85+
mkSEXPIO :: a -> IO (SEXP V)
86+
{-@ fromSEXP :: s:SEXP s -> {v:a | literalRType v == typeOf s} @-}
87+
fromSEXP :: SEXP s -> a
88+
89+
{-@ literalRType :: x:a -> {v:R.SEXPTYPE | v == literalRType x } @-}
90+
literalRType :: a -> R.SEXPTYPE
7691

77-
default mkSEXPIO :: (IsVector ty, Literal [a] ty) => a -> IO (SEXP V ty)
92+
{-
93+
default mkSEXPIO :: (IsVector ty, Literal [a] ty) => a -> IO (SEXP V)
7894
mkSEXPIO x = mkSEXPIO [x]
7995
80-
default fromSEXP :: (IsVector ty, Literal [a] ty) => SEXP s ty -> a
96+
default fromSEXP :: (IsVector ty, Literal [a] ty) => SEXP s -> a
8197
fromSEXP (fromSEXP -> [x]) = x
8298
fromSEXP _ = failure "fromSEXP" "Not a singleton vector."
99+
-}
83100

84101
-- | Create a SEXP value and protect it in current region
85-
mkSEXP :: (Literal a b, MonadR m) => a -> m (SEXP (Region m) b)
86-
mkSEXP x = acquire =<< io (mkSEXPIO x)
87-
88-
-- | Like 'fromSEXP', but with no static type satefy. Performs a dynamic
89-
-- (i.e. at runtime) check instead.
90-
fromSomeSEXP :: forall s a form. (Literal a form) => R.SomeSEXP s -> a
91-
fromSomeSEXP = fromSEXP . R.cast (sing :: Sing form)
102+
{-@ assume mkSEXP :: x:a -> m (TSEXP (Region m) (literalRType x)) @-}
103+
mkSEXP :: (Literal a b, MonadR m) => a -> m (SEXP (Region m))
104+
mkSEXP x = io (mkSEXPIO x) >>= \a -> acquire a
92105

93106
-- | Like 'fromSomeSEXP', but behaves like the @as.*@ family of functions
94107
-- in R, by performing a best effort conversion to the target form (e.g. rounds
95108
-- reals to integers, etc) for atomic types.
96-
dynSEXP :: forall a s ty. (Literal a ty) => SomeSEXP s -> a
97-
dynSEXP (SomeSEXP sx) =
98-
fromSomeSEXP $ unsafePerformIO $ case fromSing (sing :: SSEXPTYPE ty) of
109+
dynSEXP :: forall a s ty. (Literal a ty) => SEXP s -> a
110+
dynSEXP sx =
111+
fromSEXP $ unsafePerformIO $ case fromSing (sing :: SSEXPTYPE ty) of
99112
R.Char -> r1 "as.character" sx
100113
R.Int -> r1 "as.integer" sx
101114
R.Real -> r1 "as.double" sx
102115
R.Complex -> r1 "as.complex" sx
103116
R.Logical -> r1 "as.logical" sx
104117
R.Raw -> r1 "as.raw" sx
105-
_ -> return $ SomeSEXP $ R.release sx
118+
_ -> return $ R.release sx
106119

107120
{-# NOINLINE mkSEXPVector #-}
108-
mkSEXPVector :: (Storable (SVector.ElemRep s a), IsVector a)
109-
=> SSEXPTYPE a
110-
-> [IO (SVector.ElemRep s a)]
111-
-> SEXP s a
121+
{-@ mkSEXPVector :: vt:VSEXPTYPE s a -> [IO a] -> TSEXP s (vstypeOf vt) @-}
122+
mkSEXPVector :: Storable a
123+
=> SVector.VSEXPTYPE s a
124+
-> [IO a]
125+
-> SEXP s
112126
mkSEXPVector ty allocators = unsafePerformIO $ mkSEXPVectorIO ty allocators
113127

114-
mkSEXPVectorIO :: (Storable (SVector.ElemRep s a), IsVector a)
115-
=> SSEXPTYPE a
116-
-> [IO (SVector.ElemRep s a)]
117-
-> IO (SEXP s a)
128+
{-@ assume mkSEXPVectorIO :: vt:VSEXPTYPE s a -> [IO a] -> IO (TSEXP s (vstypeOf vt)) @-}
129+
{-@ ignore mkSEXPVectorIO @-}
130+
mkSEXPVectorIO :: Storable a
131+
=> SVector.VSEXPTYPE s a
132+
-> [IO a]
133+
-> IO (SEXP s)
118134
mkSEXPVectorIO ty allocators =
119-
R.withProtected (R.allocVector ty $ length allocators) $ \vec -> do
135+
R.withProtected (R.allocVector (SVector.vstypeOf ty) $ length allocators) $ \vec -> do
120136
let ptr = castPtr $ R.unsafeSEXPToVectorPtr vec
121137
zipWithM_ (\i -> (>>= pokeElemOff ptr i)) [0..] allocators
122138
return vec
123139

124140
{-# NOINLINE mkProtectedSEXPVector #-}
125-
mkProtectedSEXPVector :: IsVector b
126-
=> SSEXPTYPE b
127-
-> [SEXP s a]
128-
-> SEXP s b
141+
{-@
142+
mkProtectedSEXPVector :: vt:VSEXPTYPE s a -> [SEXP s] -> TSEXP s (vstypeOf vt)
143+
@-}
144+
mkProtectedSEXPVector :: SVector.VSEXPTYPE s a
145+
-> [SEXP s]
146+
-> SEXP s
129147
mkProtectedSEXPVector ty xs = unsafePerformIO $ mkProtectedSEXPVectorIO ty xs
130148

131-
mkProtectedSEXPVectorIO :: IsVector b
132-
=> SSEXPTYPE b
133-
-> [SEXP s a]
134-
-> IO (SEXP s b)
149+
{-@
150+
assume mkProtectedSEXPVectorIO :: vt:VSEXPTYPE s a -> [SEXP s] -> IO (TSEXP s (vstypeOf vt))
151+
ignore mkProtectedSEXPVectorIO
152+
@-}
153+
mkProtectedSEXPVectorIO :: SVector.VSEXPTYPE s a
154+
-> [SEXP s]
155+
-> IO (SEXP s)
135156
mkProtectedSEXPVectorIO ty xs = do
136157
mapM_ (void . R.protect) xs
137-
z <- R.withProtected (R.allocVector ty $ length xs) $ \vec -> do
158+
z <- R.withProtected (R.allocVector (SVector.vstypeOf ty) $ length xs) $ \vec -> do
138159
let ptr = castPtr $ R.unsafeSEXPToVectorPtr vec
139160
zipWithM_ (pokeElemOff ptr) [0..] xs
140161
return vec
141162
R.unprotect (length xs)
142163
return z
143164

144165
instance Literal [R.Logical] 'R.Logical where
145-
mkSEXPIO = mkSEXPVectorIO sing . map return
166+
mkSEXPIO = mkSEXPVectorIO (fromSing sing) . map return
146167
fromSEXP (hexp -> Logical v) = SVector.toList v
147168

169+
{-
148170
instance Literal [Int32] 'R.Int where
149171
mkSEXPIO = mkSEXPVectorIO sing . map return
150172
fromSEXP (hexp -> Int v) = SVector.toList v
@@ -256,7 +278,7 @@ instance (NFData a, Literal a la) => HFunWrap (R s a) (IO R.SEXP0) where
256278
257279
instance (Literal a la, HFunWrap b wb)
258280
=> HFunWrap (a -> b) (R.SEXP0 -> wb) where
259-
hFunWrap f a = hFunWrap $ f $! fromSEXP (R.cast sing (R.somesexp a) :: SEXP s la)
281+
hFunWrap f a = hFunWrap $ f $! fromSEXP (R.SEXP a :: SEXP s la)
260282
261283
foreign import ccall "missing_r.h funPtrToSEXP" funPtrToSEXP
262284
:: FunPtr a -> IO (SEXP s 'R.ExtPtr)
@@ -265,3 +287,4 @@ funToSEXP :: HFunWrap a b => (b -> IO (FunPtr b)) -> a -> IO (SEXP s 'R.ExtPtr)
265287
funToSEXP w x = funPtrToSEXP =<< w (hFunWrap x)
266288
267289
$(thWrapperLiterals 3 12)
290+
-}

0 commit comments

Comments
 (0)