packages feed

proarrow-0.1.0.0: test/Props/Simplex.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wno-orphans #-}

module Props.Simplex where

import Data.Falsify.ConcreteFun qualified as ConcreteFun
import Data.Fin (Fin (..), absurd, isMin)
import Data.Foldable (Foldable (..), toList)
import Data.Monoid (All (..), Ap (..))
import Data.Vec.Lazy (Vec (..), universe, zipWith)
import Data.Void qualified as Void
import Test.Falsify.Generator (Function (..), choose)
import Test.Tasty (TestTree, testGroup)
import Prelude hiding (fst, id, snd, zipWith)

import Proarrow.Category.Instance.Simplex (Forget, IsNat (..), Nat (..), Pick, SNat (..), Simplex (..))
import Proarrow.Core (Ob)

import Proarrow.Profunctor.Representable (Rep)
import Proarrow.Testing
  ( GenTotal (..)
  , ShowP (..)
  , Testable (..)
  , TestableProfunctor
  , TestableType (..)
  , TestingEqShow (..)
  , genSomeDef
  , oneElem
  , optGen
  , pattern GenNonEmpty
  )
import Proarrow.Testing.Laws
import Props.Hask ()

test :: TestTree
test =
  testGroup
    "Simplex"
    [ testCategory @Nat
    , testInitialObject @Nat
    , testTerminalObject @Nat
    , testMonoidal_ @Nat
    , testMonoid_ @Z
    , testMonoid_ @(S Z)
    , testProfunctor @(Rep Forget)
    , testProfunctor @(Rep (Pick Bool))
    ]

instance Testable Nat where
  type TestOb a = Ob a
  genSome = genSomeDef @'[Z, S Z, S (S Z), S (S (S Z))]
  showOb @a = show (singNat @a)

instance (Ob a, Ob b) => TestingEqShow (Simplex a b)
instance (Ob a, Ob b) => TestableType (Simplex a b) where
  gen = case (singNat @a, singNat @b) of
    (SZ, SZ) -> oneElem ZZ
    (SZ, SS @b') -> case gen @(Simplex Z b') of
      GenEmpty f -> GenEmpty (\(Y p) -> f p)
      GenNonEmpty gf -> GenNonEmpty (Y <$> gf)
    (SS, SZ) -> GenEmpty \case {}
    (SS @a', SS @b') -> case (gen @(Simplex a' b), gen @(Simplex (S a') b')) of
      (GenEmpty l, GenEmpty r) -> GenEmpty \case
        X f -> l f
        Y f -> r f
      (GenNonEmpty gf, GenEmpty _) -> GenNonEmpty (X <$> gf)
      (GenEmpty _, GenNonEmpty gg) -> GenNonEmpty (Y <$> gg)
      (GenNonEmpty gf, GenNonEmpty gg) -> GenNonEmpty (choose (X <$> gf) (Y <$> gg))
instance TestableProfunctor Simplex

instance (IsNat n) => TestingEqShow (Fin n)
instance (IsNat n) => TestableType (Fin n) where
  gen = case singNat @n of
    SZ -> GenEmpty absurd
    _ -> optGen (toList universe)

instance (IsNat n) => Function (Fin n) where
  function = case singNat @n of
    SZ -> fmap (ConcreteFun.map absurd Void.absurd) . function @Void.Void
    SS @m -> fmap (ConcreteFun.map isMin (maybe FZ FS)) . function @(Maybe (Fin m))

instance (TestableType a, IsNat n) => TestableType (Vec n a) where
  gen = case gen @a of
    GenEmpty ax -> case universe @n of VNil -> oneElem VNil; _ -> GenEmpty \(a ::: _) -> ax a
    GenNonEmpty ga -> GenNonEmpty (traverse (const ga) universe)
instance (TestingEqShow a, IsNat n) => TestingEqShow (Vec n a) where
  eqP VNil VNil = pure True
  eqP as bs = getAll <$> getAp (fold (zipWith (\l r -> Ap (fmap All (eqP l r))) as bs))
  showP = show . fmap ShowP

instance (IsNat n, Function a) => Function (Vec n a) where
  function = case singNat @n of
    SZ -> fmap (ConcreteFun.map (\VNil -> ()) (\() -> VNil)) . function @()
    SS @m -> fmap (ConcreteFun.map (\(x ::: xs) -> (x, xs)) (uncurry (:::))) . function @(a, Vec m a)

instance TestableProfunctor (Rep Forget)
instance TestableProfunctor (Rep (Pick Bool))