{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-} -- | -- Module: Data.Greskell.Binder -- Description: Binder monad to make binding between Gremlin variables and JSON values -- Maintainer: Toshio Ito -- -- module Data.Greskell.Binder ( -- * Types Binder, Binding, -- * Actions newBind, -- * Runners runBinder ) where import Control.Monad.Trans.State (State) import qualified Control.Monad.Trans.State as State import Data.Aeson (Value, ToJSON(toJSON), Object) import Data.Monoid ((<>)) import qualified Data.HashMap.Strict as HM import qualified Data.Text.Lazy as TL import Data.Greskell.Greskell (unsafeGreskellLazy, Greskell) -- $setup -- -- >>> import Control.Applicative ((<$>), (<*>)) -- >>> import Data.Greskell.Greskell (toGremlin) -- >>> import Data.List (sortBy) -- >>> import Data.Ord (comparing) -- >>> import qualified Data.HashMap.Strict as HashMap -- | A Monad that manages binding variables to values. -- -- >>> let binder = (,) <$> newBind (10 :: Int) <*> newBind "hoge" -- >>> let ((var_int, var_str), binding) = runBinder binder -- >>> toGremlin var_int -- "__v0" -- >>> toGremlin var_str -- "__v1" -- >>> sortBy (comparing fst) $ HashMap.toList binding -- [("__v0",Number 10.0),("__v1",String "hoge")] newtype Binder a = Binder { unBinder :: State (PlaceHolderIndex, [Value]) a } deriving (Functor, Applicative, Monad) -- | Binding between Gremlin variable names and JSON values. type Binding = Object -- | Create a new Gremlin variable bound to the given value. -- -- The value @v@ is kept in the monadic context. The returned -- 'Greskell' is a Gremlin variable pointing to the @v@. The Gremlin -- variable is guaranteed to be unique in the current monadic context. newBind :: ToJSON v => v -- ^ bound value -> Binder (Greskell v) -- ^ variable newBind val = Binder $ do (next_index, values) <- State.get State.put (succ next_index, values ++ [toJSON val]) return $ unsafePlaceHolder next_index -- | Execute the given 'Binder' monad to obtain 'Binding'. runBinder :: Binder a -> (a, Binding) runBinder binder = (ret, binding) where (ret, (_, values)) = State.runState (unBinder binder) (0, []) binding = HM.fromList $ zip (map toPlaceHolderVariableStrict [0 ..]) $ values toPlaceHolderVariableStrict = TL.toStrict . toPlaceHolderVariable -- | __This type is only for internal use.__ type PlaceHolderIndex = Int -- | __This function is only for internal use.__ -- -- Unsafely create a placeholder variable of arbitrary type with the -- given index. unsafePlaceHolder :: PlaceHolderIndex -> Greskell a unsafePlaceHolder = unsafeGreskellLazy . toPlaceHolderVariable -- | __This function is only for internal use.__ -- -- Create placeholder variable string from the index. toPlaceHolderVariable :: PlaceHolderIndex -> TL.Text toPlaceHolderVariable i = TL.pack ("__v" ++ show i)