```module Data.Logical.Let
( Let
, LetT
, Var
, free
, val
, (===)
, runLet
, runLetT
) where

import Data.Map (Map)

import Data.Logical.Knot

type VarId = Integer

newtype Let x a = Let (StateT VarId (Knot VarId x) a)

newtype LetT x m a = LetT (StateT VarId (KnotT VarId x m) a)

data Var x = Var VarId x

where
lift = LetT . lift . lift
-- Couldn't be derived for some reason

instance MonadKnot i x m => MonadKnot i x (StateT s m)
where
i *= x       = lift (i *= x)
-- So that MonadKnot can be derived for Let and LetT.

free_ :: MonadKnot VarId x m => StateT VarId m (Var x)
free_ = do
vid <- get
put (succ vid)
x <- lift \$ askKnot vid
return (Var vid x)

class MonadKnot VarId x m => MonadLet x m | m -> x
where
free :: m (Var x)

where
free = Let free_

where
free = LetT free_

val :: Var x -> x
val (Var _ x) = x

infix 1 ===

(===) :: MonadLet x m => Var x -> x -> m ()
Var vid _ === x = vid *= x

var :: MonadLet x m => x -> m (Var x)
var x = do
v <- free
v === x
return v

test = runLet \$ do
a' <- var a
a' === a
return a
where
a = 4 :: Int

acc = error "Multiple assignments to variable"

runLet :: Let x a -> a
runLet (Let ma) = fst \$ fst \$ accKnot acc \$ runStateT ma 0

runLetT :: MonadFix m => LetT x m a -> m a
runLetT (LetT ma) = liftM (fst.fst) \$ accKnotT acc \$ runStateT ma 0

```