{-# LANGUAGE OverloadedStrings,KindSignatures, GADTs, ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE ConstraintKinds #-}
module Network.JavaScript.Internal
(
JavaScript(..)
, Command()
, internalCommand
, internalConstructor
, Procedure()
, internalProcedure
, Primitive(..)
, RemoteValue(..)
, var
, Packet(..)
, AF(..)
, RemoteMonad(..)
, evalAF
, concatAF
, M(..)
, evalM
) where
import Data.Aeson (ToJSON(..), FromJSON(..))
import qualified Data.Aeson.Encoding.Internal as AI
import qualified Data.Binary.Builder as B
import Data.Text.Lazy(Text, pack)
import Data.Text.Lazy.Encoding(encodeUtf8)
import Data.String
newtype JavaScript = JavaScript Text
deriving Show
instance IsString JavaScript where
fromString = JavaScript . fromString
instance Semigroup JavaScript where
JavaScript x <> JavaScript y = JavaScript $ x <> y
instance Monoid JavaScript where
mempty = JavaScript mempty
mappend = (<>)
class Command f where
internalCommand :: JavaScript -> f ()
internalConstructor :: JavaScript -> f (RemoteValue a)
class Procedure f where
internalProcedure :: FromJSON a => JavaScript -> f a
newtype Packet a = Packet (AF Primitive a)
deriving (Functor, Applicative)
newtype RemoteMonad a = RemoteMonad (M Primitive a)
deriving (Functor, Applicative, Monad)
data Primitive :: * -> * where
Command :: JavaScript -> Primitive ()
Procedure :: FromJSON a => JavaScript -> Primitive a
Constructor :: JavaScript -> Primitive (RemoteValue a)
instance Command Packet where
internalCommand = Packet . PrimAF . Command
internalConstructor = Packet . PrimAF . Constructor
instance Procedure Packet where
internalProcedure = Packet . PrimAF . Procedure
instance Command RemoteMonad where
internalCommand = RemoteMonad . PrimM . Command
internalConstructor = RemoteMonad . PrimM . Constructor
instance Procedure RemoteMonad where
internalProcedure = RemoteMonad . PrimM . Procedure
newtype RemoteValue a = RemoteValue Int
deriving (Eq, Ord, Show)
instance ToJSON (RemoteValue a) where
toJSON = error "toJSON not supported for RemoteValue"
toEncoding rv = AI.unsafeToEncoding $ B.fromLazyByteString $ encodeUtf8 txt
where
JavaScript txt = var rv
var :: RemoteValue a -> JavaScript
var (RemoteValue n) = JavaScript $ "jsb.rs[" <> pack (show n) <> "]"
data AF :: (* -> *) -> * -> * where
PureAF :: a -> AF m a
PrimAF :: m a -> AF m a
ApAF :: AF m (a -> b) -> AF m a -> AF m b
instance Functor (AF m) where
fmap f g = pure f <*> g
instance Applicative (AF m) where
pure = PureAF
(<*>) = ApAF
concatAF :: (forall x . m x -> Maybe b) -> AF m a -> [b]
concatAF _ (PureAF _) = []
concatAF f (PrimAF p) = case f p of
Nothing -> []
Just r -> [r]
concatAF f (ApAF m1 m2) = concatAF f m1 ++ concatAF f m2
evalAF :: Applicative f => (forall x . m x -> f x) -> AF m a -> f a
evalAF _ (PureAF a) = pure a
evalAF f (PrimAF p) = f p
evalAF f (ApAF g h) = evalAF f g <*> evalAF f h
data M :: (* -> *) -> * -> * where
PureM :: a -> M m a
PrimM :: m a -> M m a
ApM :: M m (a -> b) -> M m a -> M m b
BindM :: M m a -> (a -> M m b) -> M m b
instance Functor (M m) where
fmap f g = pure f <*> g
instance Applicative (M m) where
pure = PureM
(<*>) = ApM
instance Monad (M m) where
return = PureM
(>>=) = BindM
(>>) = (*>)
evalM :: Monad f => (forall x . m x -> f x) -> M m a -> f a
evalM _ (PureM a) = pure a
evalM f (PrimM p) = f p
evalM f (ApM g h) = evalM f g <*> evalM f h
evalM f (BindM m k) = evalM f m >>= evalM f . k