Idris2/tests/idris2/reg/reg008/Vending.idr
2023-09-07 14:57:22 +01:00

126 lines
3.9 KiB
Idris

import Data.String
VendState : Type
VendState = (Nat, Nat)
data Input = COIN
| VEND
| CHANGE
| REFILL Nat
strToInput : String -> Maybe Input
strToInput "insert" = Just COIN
strToInput "vend" = Just VEND
strToInput "change" = Just CHANGE
strToInput x = if all isDigit (unpack x)
then Just (REFILL (stringToNatOrZ x))
else Nothing
export
data MachineCmd : Type ->
VendState -> VendState ->
Type where
InsertCoin : MachineCmd () (pounds, chocs) (S pounds, chocs)
Vend : MachineCmd () (S pounds, S chocs) (pounds, chocs)
GetCoins : MachineCmd () (pounds, chocs) (Z, chocs)
Refill : (bars : Nat) ->
MachineCmd () (Z, chocs) (Z, bars + chocs)
Display : String -> MachineCmd () state state
GetInput : MachineCmd (Maybe Input) state state
Pure : ty -> MachineCmd ty state state
(>>=) : {state2 : _} ->
MachineCmd a state1 state2 ->
(a -> MachineCmd b state2 state3) ->
MachineCmd b state1 state3
export
data MachineIO : VendState -> Type where
Do : {state1 : _} ->
MachineCmd a state1 state2 ->
(a -> Inf (MachineIO state2)) -> MachineIO state1
runMachine : {inState : _} -> MachineCmd ty inState outState -> IO ty
runMachine InsertCoin = putStrLn "Coin inserted"
runMachine Vend = putStrLn "Please take your chocolate"
runMachine {inState = (pounds, _)} GetCoins
= putStrLn (show pounds ++ " coins returned")
runMachine (Display str) = putStrLn str
runMachine (Refill bars)
= putStrLn ("Chocolate remaining: " ++ show bars)
runMachine {inState = (pounds, chocs)} GetInput
= do putStrLn ("Coins: " ++ show pounds ++ "; " ++
"Stock: " ++ show chocs)
putStr "> "
x <- getLine
pure (strToInput x)
runMachine (Pure x) = pure x
runMachine (cmd >>= prog) = do x <- runMachine cmd
runMachine (prog x)
namespace MachineDo
export
(>>=) : {state1 : _} ->
MachineCmd a state1 state2 ->
(a -> Inf (MachineIO state2)) -> MachineIO state1
(>>=) = Do
export
(>>) : {state1 : _} ->
MachineCmd a state1 state2 ->
Inf (MachineIO state2) ->
MachineIO state1
ma >> mb = ma >>= const mb
data Fuel = Dry | More (Lazy Fuel)
partial
forever : Fuel
forever = More forever
ignore : IO a -> IO ()
ignore mx = do x <- mx; pure ()
run : Fuel -> MachineIO mstate -> IO ()
run (More fuel) (Do c f)
= do res <- runMachine c
run fuel (f res)
run Dry p = pure ()
mutual
vend : {pounds, chocs : _} -> MachineIO (pounds, chocs)
vend {pounds = (S p)} {chocs = (S c)} = do Vend
Display "Enjoy!"
machineLoop
vend {pounds = Z} = do Display "Insert a coin"
machineLoop
vend {chocs = Z} = do Display "Out of stock"
machineLoop
refill: {pounds, chocs : _} -> (num : Nat) -> MachineIO (pounds, chocs)
refill {pounds = Z} num = do Refill num
machineLoop
refill _ = do Display "Can't refill: Coins in machine"
machineLoop
machineLoop : {pounds, chocs : _} -> MachineIO (pounds, chocs)
machineLoop = -- Do (Display "Foo") (\x => machineLoop)
do Just x <- GetInput
| Nothing => do Display "Invalid input"
machineLoop
case x of
COIN => do InsertCoin
machineLoop
VEND => vend
CHANGE => do GetCoins
Display "Change returned"
machineLoop
REFILL num => refill num
main : IO ()
main = run forever (machineLoop {pounds = 0} {chocs = 1})