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?
- 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).