constant_ :: Exp t -> Action (Exp t)
constant_ x = do
    -- either the expression is entirely constant, or has mutable variables inside it
    Value x <- x
    if null [() | Var_{} <- x] then
        -- if it's totally constant, we want to leave it that way so it works in non-eval settings (e.g. file)
        return $ return $ Value x
     else do
        -- if it's mutable, we want to share the value, but also snapshot it so that the mutable variables
        -- don't change afterwards
        v <- var
        return (Value $ val v) @= return (Value x)
        -- add the Literal so that assignment throws an error in future
        return $ return $ Value [Var_ v, Literal ""]


-- | The 'Exp' language is call-by-name, meaning you must use share to avoid evaluating an exression
--   multiple times. Using 'share', if the expression has any side effects
--   they will be run immediately, but not on subsequent uses. When defining functions operating on
--   'Exp', if you use the same input expression twice, you should share it. For example:
--
-- @
-- strPalindrom x = 'share' x $ \\x -> x '%==' strReverse x
-- @
--
--   If the expression was not shared, and @x@ read from a file, then the file would be read twice.
constant_ :: Exp t -> Action (Exp t)
constant_ x = do
    -- either the expression is entirely constant, or has mutable variables inside it
    Value x <- x
    if null [() | Var_{} <- x] then
        -- if it's totally constant, we want to leave it that way so it works in non-eval settings (e.g. file)
        return $ return $ Value x
     else do
        -- if it's mutable, we want to share the value, but also snapshot it so that the mutable variables
        -- don't change afterwards
        v <- var
        return (Value $ val v) @= return (Value x)
        -- add the Literal so that assignment throws an error in future
        return $ return $ Value [Var_ v, Literal ""]


-- | The 'Exp' language is call-by-name, meaning you must use share to avoid evaluating an exression
--   multiple times. Using 'share', if the expression has any side effects
--   they will be run immediately, but not on subsequent uses. When defining functions operating on
--   'Exp', if you use the same input expression twice, you should share it. For example:
--
-- @
-- strPalindrom x = 'share' x $ \\x -> x '%==' strReverse x
-- @
--
--   If the expression was not shared, and @x@ read from a file, then the file would be read twice.
constant_ x = do
    -- either the expression is entirely constant, or has mutable variables inside it
    Value x <- x
    if null [() | Var_{} <- x] then
        -- if it's totally constant, we want to leave it that way so it works in non-eval settings (e.g. file)
        return $ return $ Value x
     else do
        -- if it's mutable, we want to share the value, but also snapshot it so that the mutable variables
        -- don't change afterwards
        v <- var
        return (Value $ val v) @= return (Value x)
        -- add the Literal so that assignment throws an error in future
        return $ return $ Value [Var_ v, Literal ""]


-- | The 'Exp' language is call-by-name, meaning you must use share to avoid evaluating an exression
--   multiple times. Using 'share', if the expression has any side effects
--   they will be run immediately, but not on subsequent uses. When defining functions operating on
--   'Exp', if you use the same input expression twice, you should share it. For example:
--
-- @
-- strPalindrom x = 'share' x $ \\x -> x '%==' strReverse x
-- @
--
--   If the expression was not shared, and @x@ read from a file, then the file would be read twice.