{-# OPTIONS_GHC -fno-warn-missing-fields #-}
{- | A quasiquoter to help with calling <http://www.r-project.org/ R> from ghc.

-}
module RlangQQ (
    -- * the quasiquoter
    r, rChan,

    -- ** conversion of values
    -- $note
    -- values passed to R and returned by it should have types in these classes
    ToRDS, FromRDS,

    -- ** records
    -- $records
    listToRecN,
    n,

    -- ** connecting to a single R session
    -- $chans
    newRChan, newRChan', sendRcv,

    module Data.HList.CommonMain,

    -- for ghc bug regarding lookupVers2
    module GHC.TypeLits,

    -- ** TODO
    -- $TODO
    ) where

import RlangQQ.Binary (ToRDS, FromRDS)
import RlangQQ.Internal
import RlangQQ.MakeRecord
import RlangQQ.NatQQ
import Language.Haskell.TH.Quote

import Data.HList.CommonMain

import GHC.TypeLits


import Control.Concurrent




{- | 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 'FromRDS' or 'ToRDS'. Currently the relation between
where variables are used and assigned to (using @<-@) determines the 'Intent'.

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

> {-# LANGUAGE QuasiQuotes #-}
> 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: <<http://code.haskell.org/~aavogt/Rlang-QQ/examples/test.png>>

While it is only somewhat usable, you can have Rnw/Rmd documents (knitr) that
include haskell code. One example is given
<http://code.haskell.org/~aavogt/Rlang-QQ/examples/test7.html here>

-}
r = QuasiQuoter { quoteExp = \s -> do
        n <- getRlangQQ_n
        quoteRExpression2 n False ("print('');"++ s)
            -- the first expression gets dropped somewhere else
            -- this hack is easiest
        }


-- | same as @[rChan| |]@ does the same as [r|  |], except the
-- return value will be a @Chan (Record a)@.
rChan = QuasiQuoter { quoteExp = \s -> do
        n <- getRlangQQ_n
        quoteRExpression2 n True ("print('');"++ s)
            -- the first expression gets dropped somewhere else
            -- this hack is easiest
        }

{- $records

If the quasiquote assigns to variables @hs_x@ and @hs_y@, the result type will
be @IO (Record '[LVPair "x" x, LVPair "y" y])@. The types @x@ and @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]

-}


{- $chans

Variables like @ch_x@ @ch_longVariableName@ inside the quasiquote generate references
to @x@ and @longVariableName@. These variables should have type @'Chan' (a, b -> 'IO' '()')@.
'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. <http://code.haskell.org/~aavogt/Rlang-QQ/examples/test4.hs 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.

-}


-- | 'newChan' with a more restricted type
newRChan = newRChan' undefined

-- | @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@
newRChan' proxy = newChan `asTypeOf` r proxy
    where
    r :: a -> IO (Chan (a, b -> IO ()))
    r _ = undefined

-- | @y <- sendRcv c x@ sends the value @x@ using the chan @c@.
-- Provided that an @[r| |]@ quasiquote above refers to a @ch_c@,
-- the call to 'sendRcv' will eventually produce a 'Record' @y@
sendRcv :: Chan (t, b -> IO ()) -> t -> IO b
sendRcv ch e = do
    v <- newEmptyMVar
    writeChan ch (e, putMVar v)
    takeMVar v


{- $TODO

[@debugging@] 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?

[@antiquote@] doesn't do escapes, so a string '$( blah )' might end badly

[@more examples@] conversion both ways etc.

[@read NULL as Maybe?@]

[@more datatypes@] 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 + 1@
be callable by.

[@use libR.so@]
there is a <http://hackage.haskell.org/package/hR-0.1.1 hR package> which might
allow usage similar to hslua (ie. calling individual R functions)

one drawback is that it uses lists for vectors...

[@static analysis@]
(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.

-}