purescript-lua icon indicating copy to clipboard operation
purescript-lua copied to clipboard

Undefined variable in compiled code

Open Renegatto opened this issue 1 month ago • 2 comments
trafficstars

A compilation in some cases may produce invalid lua that is fails at runtime. I have not figured out what exactly causes this behavior but succeeded to catch some example.

Example

I tried my best to minimize it but can't find a way to make it smaller.

module Example (baz) where

import Effect (Effect)
import Prelude (class Monad, Unit, bind, discard, pure, unit)

baz ∷ Effect Unit
baz = bar (pure unit)

bar ∷ ∀ f. Monad f ⇒ f Unit → f Unit
bar f = do
  f
  _ ← pure [ foo f ]
  pure unit

foo ∷ ∀ f. Monad f ⇒ f Unit → f Unit
foo fn1 = do
  _ ← fn1
  fn1
  fn1
  fn1

Run via:

pslua --foreign-path . --ps-output output--lua-output-file out.lua --entry Example

Generated code

...
return {
  baz = (function()
    return function(f)
      local Bind1 = M.Effect_monadEffect.Bind1()
      local pure = M.Control_Applicative_pure(M.Effect_monadEffect.Applicative0())
      return M.Example_discard(Bind1)(f)(function()
        return M.Control_Bind_bind(Bind1)(pure({
          [1] = (function()
            return function(fn1)
              local discard1 = M.Example_discard(Bind11) -- Bind11 is not in scope
              return M.Control_Bind_bind(M.Effect_monadEffect.Bind1())(fn1)(function(  )
                return discard1(fn1)(function()
                  return discard1(fn1)(function() return fn1 end)
                end)
              end)
            end
          end)()(f)
        }))(function() return pure(M.Data_Unit_foreign.unit) end)
      end)
    end
  end)()(M.Control_Applicative_pure(M.Effect_applicativeEffect)(M.Data_Unit_foreign.unit))
}

Version

pslua: 684b8d2c32524394c51577636a258e71d00cfefb (actual master)

Renegatto avatar Oct 09 '25 19:10 Renegatto

Thanks for putting effort in reproduction and bug-reporting!

Unfortunately and strangely, I am not able to reproduce the failure as a golden test, check this PR out.

I have also tried running this command from test/ps dir:

 yura/issue-37-repro  cabal run pslua -- --foreign-path . --ps-output output --entry Golden.Issue37.Test 
PS Lua: compiling ...
Wrote linked modules to /home/yura/projects/purescript/purescript-lua/purescript-lua/test/ps/main.lua

and it produces main.lua:

local function PSLUA_runtime_lazy(name)
  return function(init)
    return function()
      local state = 0
      local val = nil
      if state == 2 then
        return val
      else
        if state == 1 then
          return error(name .. " was needed before it finished initializing")
        else
          state = 1
          val = init()
          state = 2
          return val
        end
      end
    end
  end
end
local M = {}
M.Data_Unit_foreign = { unit = {} }
M.Effect_foreign = {
  pureE = function(a)
      return function()
        return a
      end
    end,
  bindE = function(a)
      return function(f)
        return function()
          return f(a())()
        end
      end
    end
}
M.Control_Applicative_pure = function(dict) return dict.pure end
M.Control_Bind_bind = function(dict) return dict.bind end
M.Effect_monadEffect = {
  Applicative0 = function() return M.Effect_applicativeEffect end,
  Bind1 = function() return M.Effect_bindEffect end
}
M.Effect_bindEffect = {
  bind = M.Effect_foreign.bindE,
  Apply0 = function() return M.Effect_Lazy_applyEffect(0) end
}
M.Effect_applicativeEffect = {
  pure = M.Effect_foreign.pureE,
  Apply0 = function() return M.Effect_Lazy_applyEffect(0) end
}
M.Effect_Lazy_functorEffect = PSLUA_runtime_lazy("functorEffect")(function()
  return {
    map = function(f)
      return (M.Effect_applicativeEffect.Apply0()).apply(M.Control_Applicative_pure(M.Effect_applicativeEffect)(f))
    end
  }
end)
M.Effect_Lazy_applyEffect = PSLUA_runtime_lazy("applyEffect")(function()
  return {
    apply = (function()
      return function(f)
        local bind = M.Control_Bind_bind(M.Effect_monadEffect.Bind1())
        return function(a)
          return bind(f)(function(fPrime)
            return bind(a)(function(aPrime)
              return M.Control_Applicative_pure(M.Effect_monadEffect.Applicative0())(fPrime(aPrime))
            end)
          end)
        end
      end
    end)(),
    Functor0 = function() return M.Effect_Lazy_functorEffect(0) end
  }
end)
M.Golden_Issue37_Test_discard = M.Control_Bind_bind
M.Golden_Issue37_Test_foo = function(dictMonad)
  return function(fn1)
    local Bind1 = dictMonad.Bind1()
    local discard1 = M.Golden_Issue37_Test_discard(Bind1)
    return M.Control_Bind_bind(dictMonad.Bind1())(fn1)(function()
      return discard1(fn1)(function()
        return discard1(fn1)(function() return fn1 end)
      end)
    end)
  end
end
M.Golden_Issue37_Test_bar = function(dictMonad)
  return function(f)
    local Bind1 = dictMonad.Bind1()
    local pure = M.Control_Applicative_pure(dictMonad.Applicative0())
    return M.Golden_Issue37_Test_discard(Bind1)(f)(function()
      return M.Control_Bind_bind(Bind1)(pure({
        [1] = M.Golden_Issue37_Test_foo(dictMonad)(f)
      }))(function() return pure(M.Data_Unit_foreign.unit) end)
    end)
  end
end
return {
  baz = M.Golden_Issue37_Test_bar(M.Effect_monadEffect)(M.Control_Applicative_pure(M.Effect_applicativeEffect)(M.Data_Unit_foreign.unit)),
  bar = M.Golden_Issue37_Test_bar,
  foo = M.Golden_Issue37_Test_foo
}

Am I missing something?

Unisay avatar Oct 14 '25 16:10 Unisay

@Unisay thanks for the quick response!

It seems to be crucial to export explicitly. This makes the bug go for me:

- module Example (baz) where
+ module Example where

So I think this change will help to reproduce it:

- module Golden.Issue37.Test where
+ module Golden.Issue37.Test (baz) where

Renegatto avatar Oct 16 '25 09:10 Renegatto