{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-}

{-|

Module      : Stack
Description : Example using the MyState Monad
Copyright   : © Frank Jung, 2020
License     : GPL-3

Use the 'MyState' monad to implement a
<https://en.wikipedia.org/wiki/Stack_(abstract_data_type) Stack>.

Source:
<https://gist.github.com/sdiehl/8d991a718f7a9c80f54b sdiehl/state.hs>

-}

module Stack (
                Stack       -- 'Stack' as array of ints
              , empty       -- Empty 'Stack'
              , pop         -- Pop from top of 'Stack'
              , push        -- Push onto 'Stack'
              , top         -- Get element at top of 'Stack'
              , tasks       -- Run arbitrary tasks on 'Stack'
             ) where

import           MyState (MyState (..))

-- | Stack as array of intergers.
type Stack = [Int]

-- | Empty stack.
empty :: Stack
empty :: Stack
empty = []

-- | Remove element from top of stack.
pop :: MyState Stack Int
pop :: MyState Stack Int
pop = (Stack -> (Int, Stack)) -> MyState Stack Int
forall s a. (s -> (a, s)) -> MyState s a
MyState ((Stack -> (Int, Stack)) -> MyState Stack Int)
-> (Stack -> (Int, Stack)) -> MyState Stack Int
forall a b. (a -> b) -> a -> b
$ \(Int
x:Stack
xs) -> (Int
x, Stack
xs)

-- | Push element onto stack.
push :: Int -> MyState Stack ()
push :: Int -> MyState Stack ()
push Int
a = (Stack -> ((), Stack)) -> MyState Stack ()
forall s a. (s -> (a, s)) -> MyState s a
MyState ((Stack -> ((), Stack)) -> MyState Stack ())
-> (Stack -> ((), Stack)) -> MyState Stack ()
forall a b. (a -> b) -> a -> b
$ \Stack
xs -> ((), Int
aInt -> Stack -> Stack
forall a. a -> [a] -> [a]
:Stack
xs)

-- | Return element at top of stack.
top :: MyState Stack Int
top :: MyState Stack Int
top = (Stack -> (Int, Stack)) -> MyState Stack Int
forall s a. (s -> (a, s)) -> MyState s a
MyState ((Stack -> (Int, Stack)) -> MyState Stack Int)
-> (Stack -> (Int, Stack)) -> MyState Stack Int
forall a b. (a -> b) -> a -> b
$ \(Int
x:Stack
xs) -> (Int
x, Int
xInt -> Stack -> Stack
forall a. a -> [a] -> [a]
:Stack
xs)

-- | Example usage of 'MyState' 'Stack'. 'push' & 'pop' some elements onto
-- the stack, and read the current 'top' element.
tasks :: MyState Stack Int
tasks :: MyState Stack Int
tasks = do
  Int -> MyState Stack ()
push Int
1        -- populate with some data
  Int -> MyState Stack ()
push Int
2
  Int -> MyState Stack ()
push Int
3
  Int
a <- MyState Stack Int
pop      -- 3
  Int
b <- MyState Stack Int
pop      -- 2
  Int -> MyState Stack ()
push (Int
a Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
b)  -- 5
  MyState Stack Int
top           -- 5