A quasiquoter to help with calling R from ghc.
- r :: QuasiQuoter
- rChan :: QuasiQuoter
- class ToRDS a
- class FromRDS a
- listToRecN :: ListToRecN __ (n :: HNat) x r => Proxy n -> [x] -> Record r
- n :: QuasiQuoter
- newRChan :: IO (Chan (a, b -> IO ()))
- newRChan' :: a -> IO (Chan (a, b -> IO ()))
- sendRcv :: Chan (t, b -> IO ()) -> t -> IO b
- module Data.HList.CommonMain
- module GHC.TypeLits
Calls R with the supplied string. Variables in R prefixed hs_ cause
the corresponding (un-prefixed) variable to be converted. The variable(s) must
be in at least one class
ToRDS. Currently the relation between
where variables are used and assigned to (using
<-) determines the
Expressions are also supported. These must be text between $( ), just like template haskell. One condition is that the contents between the parentheses must be parseable by haskell-src-meta/haskell-src-exts. So if you find the hs_ notation unpleasant you can still interpolate using $(x).
An example of both styles is
import RlangQQ x = [0 .. 10 :: Double] main = do [r| library(ggplot2) png(file='test.png') plot(qplot( hs_x, $(map (sin . (*pi) . (/10)) x) )) dev.off() |]
You get a plot:
While it is only somewhat usable, you can have Rnw/Rmd documents (knitr) that include haskell code. One example is given here
[rChan| |] does the same as [r| |], except the
return value will be a
Chan (Record a).
conversion of values
same as Binary but should be compatible with R's
binary mode, which is for single objects
T.pack abc =>
|ToRDSRecord k __ ___ xs => ToRDS (Record xs)|
The type variables with underscores should be hidden
|ToRDS (Vector Double)|
becomes a numeric vector
|ToRDS (Vector Int32)|
|ToRDS (Vector Text)|
|(ToRDS t, ToRDS (RDA rs), ShowLabel k l2) => ToRDS (RDA (: * (LVPair k l2 t) rs))|
|ToRDS (RDA ( *))|
|IxSize i => ToRDS (Array i Int32)|
|IxSize i => ToRDS (Array i Double)|
|(ToRDS t, ShowLabel k l) => ToRDS (LVPair k l t)|
|(Source r Int32, Shape sh) => ToRDS (Array r sh Int32)|
|(Source r Double, Shape sh) => ToRDS (Array r sh Double)|
|FromRDSRec a b as' as'2 bs' => FromRDS (Record bs')|
|FromRDS (Vector Double)|
|FromRDS (Vector Int32)|
|FromRDS (Vector Text)|
|(FromRDS t, FromRDS (RDA rs), ShowLabel k l2) => FromRDS (RDA (: * (LVPair k l2 t) rs))|
|FromRDS (RDA ( *))|
|IxSize i => FromRDS (Array i Int32)|
note indices become 0-based (see
|IxSize i => FromRDS (Array i Double)|
note indices become 0-based (see
|(FromRDS t, ShowLabel k l) => FromRDS (LVPair k l t)|
|Shape sh => FromRDS (Array U sh Int32)|
|Shape sh => FromRDS (Array U sh Double)|
If the quasiquote assigns to variables
hs_y, the result type will
IO (Record '[LVPair x x, LVPair y y]). The types
y have to be
determined on the haskell side. Here is a complete example:
:set -XQuasiQuotes -XDataKinds -XNoMonomorphismRestriction
let x = [2 :: Double]
let q = [r| hs_y <- 1 + hs_x; hs_z <- 2 |]
These labels could be generated by template haskell with
$(makeLabels6 (words "y z"))
let y = Label :: Label "y"
let z = Label :: Label "z"
do o <- q; print (o .!. y ++ o .!. z :: [Double])[3.0,2.0]
convert a haskell list into a record with labels all of type "". The length
of the list is decided by the (type of the) first argument which is a
connecting to a single R session
ch_longVariableName inside the quasiquote generate references
longVariableName. These variables should have type
Chan (a, b ->
newChan can produce values of that type, but some versions with restricted
types are provided:
do x <- newRChan longVariableName <- newRChan' (undefined :: Double)
The whole input to R is re-sent each time whenever a whole set of ch_ variables is available. examples/test4.hs has an a working example shows that keeping the same R-session open is much faster, but that results may be confusing since nothing explicitly says (besides this documentation here) that the same code is re-sent.
newRChan (undefined :: Double) produces an even more restricted type than
newRChan', which can help make type errors more sensible and/or avoid
ambiguous type variable
- Write file that can be run to loading a quote into R interpreter (ie the same thing as readProcess R --no-save ...). For now it's pretty simple to just cd Rtmp and load/source things. also, return R's stdout stderr exitcode in the HList. This won't be practical for the Chan option since the stdout is getting consumed?
- doesn't do escapes, so a string '$( blah )' might end badly
- conversion both ways etc.
read NULL as Maybe?
- support things like ??, ...
call R functions as if they were defined in haskell
- This can be achievede already by doing something like
x <- newRChan [r| hs_f <- ch_x + 1 |] let f :: Double -> IO Double f xVal = (.!. (Label :: Label "f")) `fmap` sendRcv x xVal
But perhaps something can be done to generate the above code from something much shorter like:
[r| hs_f <- function(x) x + 1 |]
Can this be made to work without looking at whether there is a function() after the <-?
call hs functions as if they were defined in R
we might like to be able to have values like
f x = x + 1be callable by.
- there is a hR package which might allow usage similar to hslua (ie. calling individual R functions)
one drawback is that it uses lists for vectors...
- (optionally?) call something like codetools on the generated R code to infer result/argument types. Or perhaps translate R code into some constraints:
class RApp (x :: [*]) r
instance (UpcastNumR a b ~ r, UpcastNumR b a ~ r) => RApp [Proxy "+", a, b] r
type family UpcastNumR a b type instance UpcastNumR Double Int = Double type instance UpcastNumR Int Int = Int
the benefit here is that users could add their own RApp instances.
On the other hand, perhaps using a separate constraint solver will be less
confusing in terms of type errors (ie. failure to infer a type from R
which will happen (features like
do.call) should not complicate the types
seen on the haskell side).
or run the code first with some dummy inputs (say vectors of length 10 or so), and assume those types will continue to be the same.