{- | Persist and reload a fitted 'Transform'.

A 'Transform' is just an ordered list of named output expressions, so it
serializes through the same JSON wire format as any pipeline (see
"DataFrame.Expr.Serialize"). Save a fitted preprocessing/feature transform in one
process and reload it in another with 'applyTransform' to run inference:

> Right t <- loadTransformFromFile "scaler.json"
> let scored = applyTransform t newData
-}
module DataFrame.Transform.Serialize (
    encodeTransform,
    decodeTransform,
    saveTransformToFile,
    loadTransformFromFile,
) where

import qualified Data.Aeson as Aeson

import DataFrame.Expr.Serialize (
    decodeNamedExprs,
    encodeNamedExprs,
    loadPipelineFromFile,
    savePipelineToFile,
 )
import DataFrame.Transform (Transform (..))

-- | Encode a transform's output expressions to JSON.
encodeTransform :: Transform -> Either String Aeson.Value
encodeTransform :: Transform -> Either String Value
encodeTransform = [NamedExpr] -> Either String Value
encodeNamedExprs ([NamedExpr] -> Either String Value)
-> (Transform -> [NamedExpr]) -> Transform -> Either String Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transform -> [NamedExpr]
transformOutputs

-- | Decode a transform produced by 'encodeTransform'.
decodeTransform :: Aeson.Value -> Either String Transform
decodeTransform :: Value -> Either String Transform
decodeTransform = ([NamedExpr] -> Transform)
-> Either String [NamedExpr] -> Either String Transform
forall a b. (a -> b) -> Either String a -> Either String b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [NamedExpr] -> Transform
Transform (Either String [NamedExpr] -> Either String Transform)
-> (Value -> Either String [NamedExpr])
-> Value
-> Either String Transform
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Value -> Either String [NamedExpr]
decodeNamedExprs

-- | Encode a transform and write it to a file. No file is written on failure.
saveTransformToFile :: FilePath -> Transform -> IO (Either String ())
saveTransformToFile :: String -> Transform -> IO (Either String ())
saveTransformToFile String
fp = String -> [NamedExpr] -> IO (Either String ())
savePipelineToFile String
fp ([NamedExpr] -> IO (Either String ()))
-> (Transform -> [NamedExpr]) -> Transform -> IO (Either String ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transform -> [NamedExpr]
transformOutputs

-- | Load a transform produced by 'saveTransformToFile'.
loadTransformFromFile :: FilePath -> IO (Either String Transform)
loadTransformFromFile :: String -> IO (Either String Transform)
loadTransformFromFile String
fp = (Either String [NamedExpr] -> Either String Transform)
-> IO (Either String [NamedExpr]) -> IO (Either String Transform)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (([NamedExpr] -> Transform)
-> Either String [NamedExpr] -> Either String Transform
forall a b. (a -> b) -> Either String a -> Either String b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [NamedExpr] -> Transform
Transform) (String -> IO (Either String [NamedExpr])
loadPipelineFromFile String
fp)