Yuescript icon indicating copy to clipboard operation
Yuescript copied to clipboard

Can YUE support empty block and some logical falsity?

Open thesolitudeofnoblesoul opened this issue 4 years ago • 21 comments

Can YUE support empty block and some logical falsity?

  • I hope Yue can support empty block code act as place holder for TODO and comments. Maybe we can use keyword 'pass' like python for this task. EX:
if os == 'win'
  print 'great'
else if os == 'mac'
  pass
else
  -- TODO: implement
  pass
  • I have some personal problem with lua 'FALSITY'. Well... when work with some other languages and yue/lua at same time, I have a tenacity to make this error: "nil and false are the only false values". It would nice if lua behave with 0 and "" like other languages. I known I can write a function or macro to achieve this. But this make code inconvenience and look ugly. Can Yue introduce a real 'FALSITY' logical operator do the dirty stuff. EX:
if !val
  print 'FALSE'
if !!val
  print 'TRUE'

==> Lua

if not val or val == 0 or val == "" then
  print 'FALSE'
end
-- if val and val ~= 0 and val ~= "" then
-- or
if not (not val or val == 0 or val == "") then
  print 'TRUE'
end
  • Support nullish coalescing operator like other modern languages. Maybe we can use '??' op like javascript, C#. EX:
valA = nullValue ?? "default for A"
valB ??= "default for B"

==> Lua

local valA
if nullValue ~= nil then
  valA = "default for A"
end
local valB
if valB ~= nil then
  valB = "default for B"
end

Thanks and regards!

thesolitudeofnoblesoul avatar Oct 26 '21 10:10 thesolitudeofnoblesoul

  • Just got time to look into this issue. Nullish coalescing operator is useful, I had been ran into many cases for needing this syntax and it is now implemented by aed806476fe50899c0f01750175531ac41267b9d. And we can now write codes like:
local a, b, c, d
a = b ?? c ?? d

a ??= false

Compiles to:

local a, b, c, d
if b ~= nil then
  a = b
else
  if c ~= nil then
    a = c
  else
    a = d
  end
end
if a == nil then
  a = false
end
  • I'm not sure if the logical falsity will introduce more complexity to read the codes and the rules for logical falsity is still not clear. Like whether the empty table should also be checked too.

  • I had needed the empty block for a while when coding with Moonscript. But I ended up getting used to write codes like:

if x
  func!
else
  print("Not implemented!")

-- or

if x
  func!
else
  error("Not implemented!")

Which can better help me to avoid thinking the codes was already done by mistakes. So I think the empty block place holder might not be a good coding practice.

pigpigyyy avatar Oct 31 '21 09:10 pigpigyyy

Then why not implement a todo keyword instead? It would compile to an empty block, but would clearly indicate that the code was not complete.

Of course, having a pass keyword would make sense as well, for those cases where the code was done.

Sod-Almighty avatar Nov 16 '21 10:11 Sod-Almighty

So the todo is just the same function as pass with different word? how about compile it to something like error("todo")?

pigpigyyy avatar Nov 17 '21 01:11 pigpigyyy

Empty "todo" sections aren't supposed to kill the program. They're just placeholders. Maybe output a warning or something during compilation; that would be a good idea. But don't crash.

Sod-Almighty avatar Nov 17 '21 13:11 Sod-Almighty

I reckon allow todo to optionally accept a string argument. During compilation, print the argument thus:

todo "Validate inputs"

Compilation message:

TODO: validate inputs

Sod-Almighty avatar Nov 17 '21 13:11 Sod-Almighty

The filename and line number would be good things to print also.

Sod-Almighty avatar Nov 17 '21 13:11 Sod-Almighty

Sorry for take long time to reply when I request it! Just too busy lastly!

I thinks just support empty code block should make more sense than implement any script logic! For many reason that sometime I need sometime like 'pass' keyword of python. So I just resort to something like a noop function name 'pass' or '(->)()'

-- some rules in code base that I must follow
if not_required
  -- no "not not" if statement
  -- can not rename variable that not owned
  pass!
else
  -- do stuff here

I can not use error is a big no no for running app. Code expected to run into empty code block, result can be less idea but still function as expected, not optimize but work in many cases. And no 'print' because there a case that output is consume as result or host app not has a console to work with 'print'.

if os == 'win'
  optimize buffer
else if os == 'mac'
  -- not smooth as win but function
  pass
else
  -- good it work, it not, no deal breaker
  -- maybe look into in future but not priority for now
  pass

Another good (maybe bad) case usefull for 'pass'. Total ignore all code below it when compile. EX:

if flagOptimize
  pass
  -- for some reason can not touch code below and can not change struct code base when commit
  -- for now just pass code below and wait for person who responsible it done

  optimizeA!
  optimizeB!
  ...
  -- a lot of codes here
  -- 
  ...
  notSoGoodFunction!
  notTestFunction!

I think feature todo keyword should not be consider, it should be implement another dev tool. it not good to bloat a good 'transpiler' like YueScirpt with ambiguous feature not necessary.

Thanks and regards!

thesolitudeofnoblesoul avatar Nov 19 '21 02:11 thesolitudeofnoblesoul

Thought of a more decent way to write empty block. I updated the macro functions, and now we can make use of macro to help us better deal with the situation.

-- test.yue

macro todoInner = (module, line, msg)->
  print "TODO#{msg and ': ' .. msg or ''} in file #{module}, at line #{line}"
  {
    code: "-- TODO#{msg and ': ' .. msg or ''}" -- currently only expand the macro to "Lua code" can reserve the comments
    type: "lua"
  }

macro todo = (msg)->
  if msg
    "$todoInner $MODULE, $LINE, #{msg}" -- $MODULE and $LINE are builtin macros, which expand to current filename and current line number
  else
    "$todoInner $MODULE, $LINE"

switch os
  when 'win'
    optimize buffer
  when 'android'
    $todo
  when 'mac'
    $todo "not smooth as win but function"
  else
    $todo "good it work, it not, no deal breaker, maybe look into in future but not priority for now"

print "done"

Compile it with yue tool, and the compiler will print the notices for todo during compilation.

> $ yue test.yue                                                               
TODO in file "test.yue", at line 20
TODO: "not smooth as win but function" in file "test.yue", at line 22
TODO: "good it work, it not, no deal breaker, maybe look into in future but not priority for now" in file "test.yue", at line 24
Built test.yue

And then get the compiled source:

do
  local _exp_0 = os
  if 'win' == _exp_0 then
    optimize(buffer)
  elseif 'android' == _exp_0 then
-- TODO
  elseif 'mac' == _exp_0 then
-- TODO: "not smooth as win but function"
  else
-- TODO: "good it work, it not, no deal breaker, maybe look into in future but not priority for now"
  end
end
return print("done")

You may have a try or have a look at the commit 2ff18b4fb66d25d22e5a25fb386fe171853e0b06

And for the pass keyword usage to skip some codes, I usually multi-comment them out or write the Lua idiom like:

if flagOptimize
  return if true
  -- for some reason can not touch code below and can not change struct code base when commit
  -- for now just pass code below and wait for person who responsible it done

  optimizeA!
  optimizeB!
  ...
  -- a lot of codes here
  -- 
  ...
  notSoGoodFunction!
  notTestFunction!

pigpigyyy avatar Nov 19 '21 07:11 pigpigyyy

It really nice to have access line number in macro! If possible I would like to have access to current scope function name, parameter, signature or something like that! That would be extremely helpful to write some macro debug assert or trace! I think that we should rename 'MODULE' to 'FILENAME' and have more builtin 'FILEPATH' for more consistent. Because 'MODULE' kind of ambiguous.

About keyword pass: I usual use do return end idiom for dirty trick but it will have effect on entire function scope when I only want to apply to current scope. So something like keyword pass to skip the rest of codes current block would be helpful for me!

Thanks and regards!

thesolitudeofnoblesoul avatar Nov 22 '21 03:11 thesolitudeofnoblesoul

Renamed builtin macro $MODULE to $FILE. And found a simple way to implement the function to skip the rest codes in a block with current Yuescript.

macro skip = -> ""
do
  print 1
  <- $skip
  print 2
  print 3

print 4

generated codes:

do
  print(1)
end
return print(4)

pigpigyyy avatar Nov 23 '21 01:11 pigpigyyy

I don't understand what's going on in that code. I assume -> declares a closure? What is <-?

Sod-Almighty avatar Nov 23 '21 09:11 Sod-Almighty

It's backcall syntax taken from Livescript. They are used for unnest callbacks.

do
  (data)<- readFile "file.txt"
  (result)<- parse data
  print result

-- is the same as

do
  readFile "file.txt", (data)->
    parse data, (result)->
      print result

-- with fewer indents
-- So that

macro skip = -> ""
do
  print 1
  <- $skip
  print 2
  print 3

-- is the same as

macro skip = -> ""
do
  print 1
  $skip ->
    print 2
    print 3

pigpigyyy avatar Nov 23 '21 14:11 pigpigyyy

Ah, I see. So essentially, you're calling an empty macro and....passing the remaining instructions to it as a closure that it will never call?

Sod-Almighty avatar Nov 23 '21 18:11 Sod-Almighty

Wouldn't it be much easier to read if you just had a pass instruction?

Sod-Almighty avatar Nov 23 '21 18:11 Sod-Almighty

The only difference is that LiveScript doesn’t require parens for single-arg backcalls (a <- f)

vendethiel avatar Nov 23 '21 19:11 vendethiel

Wouldn't it be much easier to read if you just had a pass instruction?

It won't be hard to get all the functions implemented. Just wanted to show that anyone can extend this language at will without even touching the source code.

pigpigyyy avatar Nov 24 '21 01:11 pigpigyyy

The trick does not work anymore!

macro skip = -> ""

do
  print 1
  <- $skip
  print 2
  print 3

throw error:

5: failed to expand empty macro as expr
  <- $skip

I try with macro lua but It don't work too.

GokuHiki avatar Nov 06 '23 09:11 GokuHiki

You need to add a nil at the end of the code. Or the <- $skip macro will be treated as the last expression to be implicitly returned. So this example should be:

macro skip = -> ""

do
  print 1
  <- $skip
  print 2
  print 3

nil

pigpigyyy avatar Nov 07 '23 00:11 pigpigyyy

How can this work with function?

macro skip = -> ""

func = ->
  <- $skip
  print("done")
  return nil

return nil

It throws an error:

4: failed to expand empty macro as expr
  <- $skip
     ^

Thanks!

GokuHiki avatar Dec 08 '23 03:12 GokuHiki

Replacing the backcall body with return statement could help. Do you think we should get a real pass keyword into the syntax for this purpose?

macro skip = -> "return"

func = ->
  <- $skip
  print("done")
  return nil

return nil

get:

local func
func = function()
  return
end
return nil

pigpigyyy avatar Dec 08 '23 04:12 pigpigyyy

I don't think the pass keyword is really necessary! Even in Python, I rarely use it. When I use pass, I only use it as a placeholder. nothing more! Moonscript not allow empty code block but Yue allow empty code block with comment, so there not much that need for pass:

if true
  -- pass
else
  print false

GokuHiki avatar Dec 08 '23 07:12 GokuHiki