{-| Module : Test.Multivariant.Classes Description : Final tagless encoding of multivariant assignments language Copyright : (c) Anton Marchenko, Mansur Ziatdinov, 2016-2017 License : BSD-3 Maintainer : gltronred@gmail.com Stability : provisional Portability : POSIX This module provides typeclasses for final tagless encoding of multivariant assignment language. -} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeOperators #-} module Test.Multivariant.Classes ( -- * Program Program (..) , oneof -- * Corner cases , WithCornerCases (..) -- * Description , WithDescription (..) -- * Inverse , WithInvert (..) -- * Properties , WithConditions (..) -- * ProgramArrow , ProgramArrow (..) ) where import Prelude (Bool, ($), error) import Control.Arrow import Control.Category (Category) import qualified Control.Category as C import Data.Invertible.Bijection import Data.Invertible.Function import qualified Data.List as L import Data.Semigroupoid import Data.Text.Lazy (Text) -- | Program provides the most common operations class Program prog where -- | One step of transformation step :: (a <-> b) -- ^ Bijection @f :<->: g@ to be applied. If @f@ is not invertible, @g@ has to be right inverse: @f . g === id@ -> prog a b -- ^ Resulting program -- | Launches first program and feeds its output as input to the second one (sequential connection on the scheme) (~>) :: prog a b -- ^ First program -> prog b c -- ^ Second program -> prog a c -- ^ Resulting program -- | Launches programs in parallel and combines their inputs and outputs (parallel connection on the scheme) (<***>) :: prog a1 b1 -- ^ First program -> prog a2 b2 -- ^ Second program -> prog (a1,a2) (b1,b2) -- ^ Resulting program -- | Creates two variants of transformation (variants are stacked above each other on the scheme) (<+++>) :: prog a b -- ^ First variant -> prog a b -- ^ Second variant -> prog a b -- ^ Result -- | Simple wrapper around '(<+++>)' oneof :: Program prog => [prog a b] -> prog a b oneof = L.foldl1' (<+++>) -- | Program that can be inverted class Program prog => WithInvert prog where -- | Inversion invert :: prog a b -> prog b a -- | Program that has corner cases class Program prog => WithCornerCases prog where -- | Supply corner cases for some step withCornerCases :: prog a b -- ^ Program -> ([a], [b]) -- ^ Corner cases to check for inputs (some test will feed such input) and outputs (some test will have this result) -> prog a b -- ^ Program with corner cases -- | Program that has properties (*not implemented yet*) class Program prog => WithConditions prog where -- | Supply precondition and postcondition withConditions :: prog a b -- ^ Program -> (a -> Bool, b -> Bool) -- ^ Pre- and postcondition -> prog a b -- ^ Program with properties to be tested -- | Program that has description class Program prog => WithDescription prog where -- | Supply description for some step withDescription :: prog a b -- ^ Program -> Text -- ^ Its description in natural language -> prog a b -- ^ Program with description -- | Embed program into arrow newtype ProgramArrow p a b = ProgramArrow { getProgram :: p a b } instance Program prog => Semigroupoid (ProgramArrow prog) where o b a = ProgramArrow $ getProgram a ~> getProgram b instance Program prog => Category (ProgramArrow prog) where id = ProgramArrow $ step id (.) = o instance Program prog => Arrow (ProgramArrow prog) where arr f = ProgramArrow $ step (f :<->: error "Use biarr instead of arr") a *** b = ProgramArrow $ getProgram a <***> getProgram b -- instance IsoProfunctor Step where -- dimap f g (Step h as bs t) = Step (dimap f g h) (biFrom f <$> as) (biTo g <$> bs) t -- -- dimap f g (a :>>> b) = (dimap f g a) :>>> (dimap f g b) -- -- dimap f g (a :<***> b) = (dimap f g a) :<***> (dimap f g b) -- -- dimap f g (a :<+++> b) = (dimap f g a) :<+++> (dimap f g b) -- instance IsoStrong Step where -- -- first' (Step h as bs t) = Step _ _ _ _