packages feed

ruin-0.1.0.0: test/Strictness.hs

{-# Language BangPatterns #-}
{-# Language DataKinds #-}
{-# Language ExplicitForAll #-}
{-# Language FlexibleContexts #-}
{-# Language MultiParamTypeClasses #-}
{-# Language OverloadedLabels #-}
{-# Language PolyKinds #-}
{-# Language QuasiQuotes #-}
{-# Language TemplateHaskell #-}
{-# Language TypeApplications #-}
{-# Language TypeFamilies #-}
{-# Language TypeOperators #-}
{-# Language ViewPatterns #-}

module Strictness (test) where

import           Data.Functor.Compose
import           Data.Functor.Identity
import           Test.Hspec

import           Data.Ruin
import           Data.Ruin.Ancillaries (GiveAllItHas(..),Pair(..),genericBuild,genericBuildNonStrict,genericExtricate1,rupEval,rupNonStrict)
import           Data.Ruin.Eval (Eval(Done),runEval)

import qualified StrictCheck
import           StrictnessTypes

sameStrictness :: forall a b c. StrictCheck.SSerial a => (a -> b) -> (a -> c) -> IO ()
sameStrictness f g = StrictCheck.sameStrictness f g d >>= (`shouldBe` True)
  where
  d = 1000   -- this depth is exhausive for our examples here

nonstrict :: forall a b. StrictCheck.SSerial a => (a -> b) -> IO ()
nonstrict f = sameStrictness f (const ())

cei :: a -> Compose Eval Identity a
cei = pure

test :: Spec
test = do
  describe "Non-strict" $ do
    it "`Done'" $ do
      nonstrict @() Done

    it "`extricate1' for singleton records" $ do
      nonstrict @("s" :@ ()) (extricate1 #s)

    it "`genericBuildNonStrict'" $ do
      let eta t = ((genericBuildNonStrict . MkGiveAllItHas) `asTypeOf` id) t
      nonstrict @L eta
      nonstrict @XY eta

    it "`rup' for ()'" $ do
      let eta t = (rup `asTypeOf` id) t
      nonstrict @() eta

    it "`rupNonStrict' for tuples'" $ do
      let eta t = (rupNonStrict `asTypeOf` id) t
      nonstrict @() eta
      nonstrict @(L,R) eta
      nonstrict @XY eta

    it "`genericBuild' for newtypes" $ do
      nonstrict @NT ((genericBuild . MkGiveAllItHas) `asTypeOf` cei)

    it "`rupEval' for singleton records'" $ do
      nonstrict @("s" :@ ()) (rupEval `asTypeOf` pure)

  describe "Only strict enough to ensure the necessary fields exist" $ do
    it "`genericExtricate1'" $ do
      sameStrictness (genericExtricate1 #l) $ \MkL{} -> ()
      sameStrictness (genericExtricate1 #x) $ \MkXY{} -> ()
      sameStrictness (genericExtricate1 #y) $ \MkXY{} -> ()
      sameStrictness (genericExtricate1 #nt) $ \MkNT{} -> ()
      nonstrict @NT (genericExtricate1 #nt)

    describe "`extricate1' for" $ do
      it "singleton records" $ do
        nonstrict @("s" :@ ()) (extricate1 #s)

      it "tuples" $ do
        sameStrictness @(L,R) (extricate1 #l) $ \(MkL{},_) -> ()
        sameStrictness @(L,R) (extricate1 #r) $ \(_,MkR{}) -> ()
        sameStrictness @((XY,(L,R)),NT) (extricate1 #r) $ \((_,(_,MkR{})),_) -> ()
        sameStrictness @(XY,L,R,NT) (extricate1 #r) $ \(_,_,MkR{},_) -> ()

      it "`Pair'" $ do
        sameStrictness @(Pair L R) (extricate1 #l) $ \(MkPair MkL{} _) -> ()
        sameStrictness @(Pair L R) (extricate1 #r) $ \(MkPair _ MkR{}) -> ()
        sameStrictness @(XY `Pair` (L `Pair` R) `Pair` (NT `Pair` R))
          (extricate1 #r)
          (\((_ `MkPair` (_ `MkPair` MkR{})) `MkPair` _) -> ())

    it "`genericBuild'" $ do
      let eta t = ((genericBuild . MkGiveAllItHas) `asTypeOf` cei) t
      sameStrictness eta $ \MkL{} -> ()
      sameStrictness eta $ \MkXY{} -> ()
      sameStrictness eta $ \MkNT{} -> ()
      nonstrict @NT eta

    it "`rupEval' for non-() tuples" $ do
      let eta t = (rupEval `asTypeOf` pure) t
      nonstrict @() eta
      sameStrictness eta $ \(MkL{},MkR{}) -> ()

  describe "`rna' quasiquoter" $ do
    it "always has a data constructor" $ do
      nonstrict (\ ~[rna||] -> ())
      nonstrict (\ ~[rna|x|] -> () `asTypeOf` x)
      nonstrict (\ ~[rna|x y|] -> () `asTypeOf` x `asTypeOf` y)

      sameStrictness (\[rna||] -> ()) id
      sameStrictness (\[rna|x|] -> () `asTypeOf` x) id
      sameStrictness (\[rna|x y|] -> () `asTypeOf` x `asTypeOf` y) id

    it "is strict in a field if and only if it has a bang annotation" $ do
      sameStrictness (\(rfrom @XY -> [rna| _@x _@y|]) -> ()) (extricate1 #x)
      sameStrictness (\(rfrom @XY -> [rna|!_@x _@y|]) -> ()) (runEval . extricate1 #x)

      nonstrict (\(rfrom @L -> ~[rna| _@l|]) -> ())
      sameStrictness (\(rfrom @L -> [rna| _@l|]) -> ()) (extricate1 #l)
      sameStrictness (\(rfrom @L -> [rna|!_@l|]) -> ()) (runEval . extricate1 #l)