Yuescript
Yuescript copied to clipboard
Can YUE support empty block and some logical falsity?
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!
- 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.
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.
So the todo is just the same function as pass with different word? how about compile it to something like error("todo")?
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.
I reckon allow todo to optionally accept a string argument. During compilation, print the argument thus:
todo "Validate inputs"
Compilation message:
TODO: validate inputs
The filename and line number would be good things to print also.
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!
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!
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!
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)
I don't understand what's going on in that code. I assume -> declares a closure? What is <-?
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
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?
Wouldn't it be much easier to read if you just had a pass instruction?
The only difference is that LiveScript doesn’t require parens for single-arg backcalls (a <- f)
Wouldn't it be much easier to read if you just had a
passinstruction?
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.
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.
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
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!
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
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