{-# LANGUAGE Safe #-}
{-|
Module      : Data.IParser
Description : Bidirectional parsing
Copyright   : (c) Mats Rauhala, 2020
License     : BSD-3
Maintainer  : mats.rauhala@iki.fi
Stability   : experimental
Portability : POSIX

Let's assume we have a parser like the following

@
int :: Parser (ReaderT String Maybe) (Writer [Int]) Int Int
int = parser (ReaderT readMaybe) (\x -> x <$ tell [show x])
@

Then you can use the parser for parsing:

@
> runReaderT (decode int) "3"
Just 3
@

Or for encoding:

@
> execWriter (encode int 3)
["3"]
@

Or combine both of them

@
> runReaderT (decode int) $ head $ execWriter $ encode int 3
Just 3
@
-}
module Data.IParser where

import Data.Profunctor

-- | The core bidirectional parser type
--
-- See the module for usage example
--
data IParser r w c a =
  IParser { decoder :: r a -- ^ Return a value "a" in context "r".
          , encoder :: Star w c a -- ^ Generate a value "a" from the initial object "c" in the context "w"
          }

-- | Smart constructor for the parser
parser
  :: r a -- ^ The parser
  -> (c -> w a) -- ^ The encoder
  -> IParser r w c a
parser dec enc = IParser { decoder = dec, encoder = Star enc }

-- | Extract the decoder from the parser
decode :: IParser r w c a -> r a
decode = decoder

-- | Extract the encoder from the parser
encode :: IParser r w c a -> c -> w a
encode = runStar . encoder

-- | Record accessor helper
--
-- Due to the nature of the parser, the encoder gets the full record type, when
-- it should only focus on a specific part.
--
-- @
-- data Person = Person { name :: String, age :: Int }
--
-- person :: IParser r w Person Person
-- person =
--  Person <$> name .= string
--         <*> age .= int
-- @
(.=) :: (Functor r, Functor w) => (c -> c') -> IParser r w c' a -> IParser r w c a
(.=) = lmap

instance (Functor w, Functor r) => Functor (IParser r w c) where
  fmap f i = IParser { decoder = f <$> decoder i
                     , encoder = f <$> encoder i
                     }

instance (Applicative w, Applicative r) => Applicative (IParser r w c) where
  pure x = IParser { decoder = pure x, encoder = pure x }
  fx <*> x = IParser { decoder = decoder fx <*> decoder x, encoder = encoder fx <*> encoder x }

instance (Functor r, Functor w) => Profunctor (IParser r w) where
  dimap f g i = IParser { decoder = g <$> decoder i, encoder = dimap f g (encoder i) }