Page 3 of 3

Re: One Line If Statements

Posted: 04 Sep 2021, 10:05
by raron
SandyClams wrote:
01 Sep 2021, 13:04
if you want a one-line if in v2 you can just use a short-circuit expression to do it.

Code: Select all

(DoMsg) && MsgBox(True)
(SkipMessage) || MsgBox(True)
Thanks for the reply.

I have to say I don't think that's more clear though. Where's the "if"? Implied? (I like the ternary operator better then).
Anyway, it is what it is (or I guess, is not; no one-liners). It's just one very unexpected snag to encounter.

Re: One Line If Statements

Posted: 03 Sep 2023, 09:08
by eugenesv
Would be nice to have one-liners so that you could nicely align short functions/statements in a tabular fashion

Re: One Line If Statements

Posted: 03 Sep 2023, 17:29
by TAC109
You can already. See for example the ternary operator.

Code: Select all

(A = B) ? C := D : E := F

(A = B) ? C := D : 0        ; True branch only 
Cheers

Re: One Line If Statements

Posted: 04 Sep 2023, 00:14
by eugenesv
How do you fit a named/hotkey function there? Or a loop? The issues is more universal
(and the extra zero isn't clean)

Re: One Line If Statements

Posted: 04 Sep 2023, 00:59
by TAC109
You asked how to have short one-liners so as to line up in a tabular manner, and what I showed will enable this to be done. Loops etc will need a more traditional approach.

Cheers

Re: One Line If Statements

Posted: 04 Sep 2023, 01:07
by eugenesv
I also specifically mentioned functions there, so what you showed will actually not enable it

Re: One Line If Statements

Posted: 04 Sep 2023, 02:19
by TAC109
Oh, you can include functions in the condition/true/false parts of the tertiary operator.

Cheers

Re: One Line If Statements

Posted: 04 Sep 2023, 02:23
by eugenesv
How would you include these functions?

Code: Select all

hk1(ThisHotkey) {SendInput('{Blind}{NumpadDiv}')}
hk2(ThisHotkey) {SendInput('{Blind}{NumpadDiv}')}
hk3(ThisHotkey) {SendInput('{Blind}{NumpadMult}')}

Re: One Line If Statements

Posted: 04 Sep 2023, 02:37
by Descolada
@eugenesv, one-liner functions are already available in v2 as fat arrow functions:
Sum(a, b) => a+b
or from your example
hk1(ThisHotkey) => SendInput('{Blind}{NumpadDiv}')

At one point there comes the question whether a one-liner is worth the terseness over readability and debuggability.

Consider this hotkey:
x::MouseMove(100,100)
I would say this is understandable at a glance: a hotkey which moves the cursor to a specific point. This is quite fine, as is the fat arrow function example above.

What about this one:
x::MouseGetPos(&X, &Y), X=100 && Y=100 ? MouseMove(200, 200) : MouseMove(100, 100)
It is still understandable if one is familiar with multi-statement expressions and ternaries. However I would argue that the following is more readable, easier for other people (especially beginners) to understand, and easier to debug (you can set break-points for the separate lines and then inspect variable contents etc):

Code: Select all

x::
{
    MouseGetPos(&X, &Y)
    if X = 100 and Y = 100
        MouseMove(200, 200)
    else 
        MouseMove(100, 100)
}
This one?
x::Looper:=((n) => --n>0 && (MouseGetPos(&X, &Y), X=100 && Y=100 ? MouseMove(200, 200) : MouseMove(100, 100), Sleep(250), Looper(n-1))), Looper(10)
I would hate to debug that, and understanding it takes considerable effort. I would much prefer

Code: Select all

x::
{
    n := 10
    while --n > 0 {
        MouseGetPos(&X, &Y)
        if X = 100 and Y = 100
            MouseMove(200, 200)
        else 
            MouseMove(100, 100)
        Sleep(250)
    }
}
So the questions is, what are possible common scenarios where you would desire a one-liner loop, where readability would still be okay?

Re: One Line If Statements

Posted: 04 Sep 2023, 04:34
by eugenesv
Descolada wrote:
04 Sep 2023, 02:37
@eugenesv, one-liner functions are already available in v2 as fat arrow functions:
Nice, thanks a bunch, wasn't aware of that syntax improvement (was searching the forum for anon functions, but missed these)
Descolada wrote:
04 Sep 2023, 02:37
At one point there comes the question whether a one-liner is worth the terseness over readability and debuggability.
The beauty of tabular alignment that one-liners enable is in this syntax, not in the ternary mess (which is harder to read, words serve a better purpose vs. commas etc)
  • Note that X hotkeys are aligned so it's immediately visible that the key is the same, and the diff in modifiers is also apparent due to them being in the same column
  • then the rest of syntax due to tabular alignment is also very easy to read - you can visually skip repeating statements and only focus on the diffs since there is no horizontal jumps
  • with some flexible tabstop plugin you don't have to maintain many vertical alignments, just place tabs in the right places

Code: Select all

^  x :: {                         MouseMove( 1,  5) }
 ! x :: { MouseGetPos(&X, &Y)
         if	X = 100 and Y = 100	{ MouseMove( 1,  1)
  } else if	X = 200            	{ MouseMove( 2,  2)
  } else if	            Y = 200	{ MouseMove(23, 23)
  } else   	                   	{ MouseMove( 4,  4)
  }
}
But also some short try/catch statemens shouldn't occupy many lines, same for short loops and everything else


And to your loop question: this shouldn't take 3 lines

Code: Select all

loop parse "2345qwertasdfzxcvb" {
    HotKey(pre " & " vk[A_LoopField], hkNumpad←)
  }

Re: One Line If Statements

Posted: 04 Sep 2023, 21:00
by lexikos
@eugene
Why shouldn't it take 3 lines?

This doesn't take 3 lines...

Code: Select all

([((n, &v) => n(&v) && MsgBox(v)).Bind(StrSplit("2345qwertasdfzxcvb").__enum(1))*])

Re: One Line If Statements

Posted: 05 Sep 2023, 00:52
by eugenesv
It shouldn't take 3 lines because it's a waste of precious vertical space and in case of repeated commands it breaks tabular alignment

I mean, you already have dedicated syntax {}, why does it require newlines?

Your example is unreadable, so that's not the price I'd pay to save 2 lines, I'd like this feature to make my config more readable, not less :)

Re: One Line If Statements

Posted: 05 Sep 2023, 00:55
by Descolada
eugenesv wrote:
04 Sep 2023, 04:34
The beauty of tabular alignment that one-liners enable is in this syntax, not in the ternary mess (which is harder to read, words serve a better purpose vs. commas etc)
Beauty is in the eyes of the beholder I guess, because the example you brought might in this case be easier to comprehend, but gets messy if you try to add something to a specific line (then you'd have to tabulate all the other lines as well) and would probably make the overall code-base less even. Wikipedia has a nice section on the arguments against vertical alignment

But I agree that words are easier to read than cryptic symbols. If one-line if statements were allowed, then perhaps that would be a chance to remove the ternary altogether? I mean if val := if (a > b) then { a } else { b } were allowed then val := a > b ? a : b would be superfluous (as is the case in Rust)...

I think I'd prefer to stick with ternaries, and add guards or something similar:

Code: Select all

val :=
	| a > b => a
	| otherwise => b
And to your loop question: this shouldn't take 3 lines

Code: Select all

loop parse "2345qwertasdfzxcvb" {
    HotKey(pre " & " vk[A_LoopField], hkNumpad←)
  }
You can limit it to two:

Code: Select all

loop parse "2345qwertasdfzxcvb"
    HotKey(pre " & " vk[A_LoopField], hkNumpad←)
Imperative languages such as AHK tend to make more sense when more lines are used, as in this case it's easy to comprehend that one line will be looped some number of iterations. You could adopt a more functional approach instead:
StrSplit("2345qwertasdfzxcvb").ForEach((A_LoopField) => HotKey(pre " & " vk[A_LoopField], hkNumpad←))
Where ForEach is added to Array.Prototype (see example).

Re: One Line If Statements

Posted: 05 Sep 2023, 01:33
by eugenesv
Descolada wrote:
05 Sep 2023, 00:55
Beauty is in the eyes of the beholder I guess, because the example you brought might in this case be easier to comprehend, but gets messy if you try to add something to a specific line (then you'd have to tabulate all the other lines as well)
You wouldn't with the flexible tabstop plugin I mentioned (also listed in the wiki), it retabulates a table on changes in any line. Otherwise it'd be a nightmare to do any edits
Descolada wrote:
05 Sep 2023, 00:55
and would probably make the overall code-base less even. Wikipedia has a nice section on the arguments against vertical alignment
Duh, beauty takes effort and tools, though that section misses the point that even when it breaks down you'll get back to the same ugly unaligned space like before! (btw, elastic tabstops in theory can also work with proportional fonts, though that's not been implemented in any popular code editor afaik)
And to your loop question: this shouldn't take 3 lines
You can limit it to two:

Code: Select all

loop parse "2345qwertasdfzxcvb"
    HotKey(pre " & " vk[A_LoopField], hkNumpad←)
This would work with significant whitespace, otherwise it's brittle (what if you add another line later?), so on the contrary I've added {} everywhere to avoid this source of bugs. Unfortunately that extra {syntax} doesn't buy me no newlines
Imperative languages such as AHK tend to make more sense when more lines are used, as in this case it's easy to comprehend that one line will be looped some number of iterations. You could adopt a more functional approach instead:
StrSplit("2345qwertasdfzxcvb").ForEach((A_LoopField) => HotKey(pre " & " vk[A_LoopField], hkNumpad←))
Where ForEach is added to Array.Prototype (see example).
this gives me 'Error: This parameter declaration conflicts with an existing built-in variable.' for A_LoopField. Or do you mean I can only use it with your linked library?

Re: One Line If Statements

Posted: 05 Sep 2023, 01:41
by Descolada
eugenesv wrote:
05 Sep 2023, 01:33
this gives me 'Error: This parameter declaration conflicts with an existing built-in variable.' for A_LoopField. Or do you mean I can only use it with your linked library?
Sorry, I didn't test it, it seems you need to rename A_LoopField to something else then. You don't have to use the linked library, it's fairly simple to define it yourself:

Code: Select all

Array.Prototype.DefineProp("ForEach", {call:_ArrayForEach})
/**
 * Applies a function to each key/value pair in the Array.
 * @param func The callback function with arguments Callback(value[, key, array]).
 * @returns {Array}
 */
_ArrayForEach(this, func) {
    if !HasMethod(func)
        throw ValueError("ForEach: func must be a function", -1)
    for i, v in this
        func(v, i, this)
    return this
}

StrSplit("2345qwertasdfzxcvb").ForEach((val, *) => MsgBox(val "`n")) 

Re: One Line If Statements

Posted: 05 Sep 2023, 03:35
by neogna2
Interesting discussions! @eugenesv can you name/link the flexible tabstop plugin? I'd like to try it, to get a feel for the pros/cons in Descolada's wikipedia link.

A way to "onelinerize" if-else if and loop segments is to go for jeeswg's lineseparator symbol idea. DIY: pick your lineseparators, save scripts as .ahkx and set up that custom extension to run a preprocessor script that converts lineseparators, saves the output AutoHotkey code and runs it. For example

loop parse "2345qwertasdfzxcvb" _{HotKey(pre " & " vk[A_LoopField], hkNumpad←)}_
with a preprocessor that does StrReplace(MyCode, "_{", "`n{`n") and StrReplace(MyCode, "}_", "`n}`n").
More complex parsing and conversion needed if the code will ever contain literal strings with the same characters of course.

Similarly " _ " (whitespace-enclosed underscore) and StrReplace(MyCode, " _ ", "`n").

A single underscore currently has no special meaning in AutoHotkey yet is visually distinct and easy to type so is a good candidate if a lineseparator were ever to be officially added.

Built in preprocessing support could use #PreProcess <preprocessor script filepath> where that script contains a function that is called with the current script's source as parameter and returns processed code to execute.
eugenesv wrote:
04 Sep 2023, 04:34
But also some short try/catch statemens shouldn't occupy many lines
Agree, I have wished for Try()

Re: One Line If Statements

Posted: 05 Sep 2023, 05:43
by eugenesv
Thank @Descolada, will check it out
neogna2 wrote:
05 Sep 2023, 03:35
Interesting discussions! @eugenesv can you name/link the flexible tabstop plugin? I'd like to try it, to get a feel for the pros/cons in Descolada's wikipedia link.
I'm using this fork with a debouncer as otherwise the plugin is too slow on large tables since it inserts spaces, not just tabs, so it's not a proper implementation (and also breaks on proportional fonts), but then there is no proper implementation of this alignment awesomeness anywhere :(


neogna2 wrote:
05 Sep 2023, 03:35
A way to "onelinerize" if-else if and loop segments is to ... run a preprocessor script
I think if you go down that route of an extra build step, then it should be way more convenient than requiring sprinkles of _ everywhere