packages feed

variant-1.0: src/bench/Main.hs

{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Main (main) where


import Data.Variant
import Data.Variant.ContFlow

import Criterion
import Criterion.Main (defaultMain)
import Test.QuickCheck
import Control.DeepSeq
import GHC.Generics


data Node a
   = NValue a
   | NPlus  (Node a) (Node a)
   | NMinus (Node a) (Node a)
   deriving (Generic,NFData)

instance Arbitrary a => Arbitrary (Node a) where
   arbitrary = do
      n <- getSize
      if n == 0
         then (NValue <$> arbitrary)
         else oneof
            [ resize (n-1) (NPlus  <$> arbitrary <*> arbitrary)
            , resize (n-1) (NMinus <$> arbitrary <*> arbitrary)
            ]

evalNode :: Num a => Node a -> a
evalNode (NValue v)   = v
evalNode (NPlus a b)  = evalNode a + evalNode b
evalNode (NMinus a b) = evalNode a - evalNode b


------------------------------------------
-- Variant
------------------------------------------

data Plus a     = Plus a a  deriving (Generic,NFData)
data Minus a    = Minus a a deriving (Generic,NFData)
newtype Value a = Value a   deriving newtype (NFData)

newtype VariantNode a
   = VariantNode (V '[Value a, Plus (VariantNode a), Minus (VariantNode a)])

deriving newtype instance (NFData a) => NFData (VariantNode a)

evalVariantNode :: Num a => VariantNode a -> a
evalVariantNode (VariantNode (V (Value v)))   = v
evalVariantNode (VariantNode (V (Plus a b)))  = evalVariantNode a + evalVariantNode b
evalVariantNode (VariantNode (V (Minus a b))) = evalVariantNode a - evalVariantNode b
evalVariantNode _                             = undefined

evalVariantNodeSafe :: Num a => VariantNode a -> a
evalVariantNodeSafe (VariantNode v) = toCont v >::>
   ( \(Value x)   -> x
   , \(Plus a b)  -> evalVariantNodeSafe a + evalVariantNodeSafe b
   , \(Minus a b) -> evalVariantNodeSafe a - evalVariantNodeSafe b
   )

nodeToVariantNode :: Node a -> VariantNode a
nodeToVariantNode (NValue a)   = VariantNode (toVariantAt @0 (Value a))
nodeToVariantNode (NPlus a b)  = VariantNode (toVariantAt @1 (Plus (nodeToVariantNode a) (nodeToVariantNode b)))
nodeToVariantNode (NMinus a b) = VariantNode (toVariantAt @2 (Minus (nodeToVariantNode a) (nodeToVariantNode b)))

main :: IO ()
main = do
   let
      evalEnv n = do
         tree1 <- generate (resize n (arbitrary :: Gen (Node Int)))
         let tree2 = nodeToVariantNode tree1
         return  (n,tree1,tree2)

      evalTest (n,tree1,tree2) = bgroup ("Tree Eval at size=" ++ show n)
         [ bench "ADT"                      $ whnf evalNode tree1
         , bench "Variant ADT - V"          $ whnf evalVariantNode tree2
         , bench "Variant ADT - Safe match" $ whnf evalVariantNodeSafe tree2
         ]


   defaultMain
      [ env (evalEnv 10) evalTest
      ]