import Control.Monad (ap, liftM)
import Data.IORef (IORef)
import System.Posix (otherExecuteMode)

data Test = Test
  { a :: Int
  , b :: Int
  , c :: Int
  }
  deriving (Show)

data Free f a = Pure a | Free (f (Free f a))

instance (Functor f) => Functor (Free f) where
  fmap = liftM

-- fmap f m = m >>= pure . f

instance (Functor f) => Applicative (Free f) where
  pure = Pure
  (<*>) = ap

instance (Functor f) => Monad (Free f) where
  (Pure a) >>= f = f a
  (Free cont1) >>= f = Free $ (>>= f) <$> cont1

data StateOps s c = GetState (s -> c) | PutState s c deriving (Functor)

type FreeState s = Free (StateOps s)

--                                           ╭----- Technically `x` can only ever be `Free f a`,
--                                           ↓      but using "forall" makes certain guarantees that we desire
interpret :: (Monad m, Functor f) => (forall x. f x -> m x) -> Free f a -> m a
interpret _transform (Pure a) = pure a
interpret transform (Free cont) = transform cont >>= interpret transform

-- @Doboz, where does the original state go? isn't Put just put some value in?
-- We may need some of the originals, no? That's what the s does, isnt' it?
-- For the _s (Free StatePut s' m)
-- then you don't need _s, because it gets overwritten, no? not just appended? no, that's Writer :catthink:, But how does StatePut know of the original s? Or you reduced the computation until StatePut has s built in? Why would you need the original s in StatePut?
-- So another Get in m can get values from state. How does that relate to Put? It's continuation passing style, so Put itself only contains what needs to happen after the current action.

-- Fibonacci S3 database when?

-- Kotlin mentioned letsgoooo

-- @Doboz, qq: can we later generate haskell source code from these free constructors? Like macro definitions at rust...
-- But you already have haskell source code? I mean like extracting the data constructors and compile it to the monad you want to use. Like a framework. So you'd get equivalent code, but ✨faster✨? yes. I guess it's not impossible. You could also just use a typeclass instead of a free monad lmfao
-- Always the typeclasses, but in case some random weirdos do that, can I fix it purely on the syntax level?..
-- I guess there's nothing stopping you. You can actually do it right in haskell through template haskell. from what I've seen, it's actually more powerful than Rust's macros. it's pretty cool
-- hmmm, can this be the goal of the usage of free here?
-- Haskell frameworks
-- no

-- Space