mIRC Home    About    Download    Register    News    Help

Print Thread
Joined: Feb 2003
Posts: 2,812
Raccoon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,812
One of the frustrating hurtles with writing a good script, is making it easy to configure and expand upon by third-party users.

I wish to propose a solution that would allow greater ability for modular coding while not being too outlandish in expectation.

SUB [-l] SubRoutineName

Similar to the ALIAS prefix in the Remotes section, but different in the following aspects:

* All local variables are preserved from the calling Alias or Event.

* All local variables set will become available to the calling Alias or Event when the SubRoutine returns.

* All event identifiers are preserved from the calling Event (I know this is already mostly done for Aliase called inside Events).

GoSub SubRoutineName

* Similar to calling an alias.


In actuality, the SUB prefix is not entirely necessary, as the GoSub command can be used in tandem with the existing ALIAS prefix and namespace. The only difference being that Aliases called with GoSub will inherit the caller's local variable scope.

This would be exceedingly useful for setting CONSTANTS and USER MODIFIABLE SCRIPT SECTIONS, placed at the beginning of the script file, or in a file separate from the main body of code.

Examples:
Code:
SUB -l SET_CONSTANTS {
  ; Modify these values as you see fit.
  var %bot.name = MyGoatBot
  var %bot.owner = MeMyselfI
}

; ...

; ...

On *:PART:#: {
  GoSub SET_CONSTANTS
  if ($me == %bot.name) && ($nick == %bot.owner) {
    part $chan wait for me!
  }
}


I know this is a pretty weak example, as you could easily set these two variables globally, but when working in a multi-server environment and especially multi-socket environment, this type of variable inheritance would be ideal.

The main attraction is that you don't have to pass all sorts of values to the Alias, and then retrieve them through use of $1 $2 3- etc. They are already available. (ByRef!)


Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Joined: Oct 2004
Posts: 8,330
Hoopy frood
Offline
Hoopy frood
Joined: Oct 2004
Posts: 8,330
You could just use /set -u %var. Without a number after -u, it will unset after the script/event and will work throughout aliases.

Code:
alias SET_CONSTANTS {
  ; Modify these values as you see fit.
  set -u %bot.name MyGoatBot
  set -u %bot.owner MeMyselfI
}

; ...

; ...

On *:PART:#: {
  SET_CONSTANTS
  if ($me == %bot.name) && ($nick == %bot.owner) {
    part $chan wait for me!
  }
}


Invision Support
#Invision on irc.irchighway.net
Joined: Feb 2003
Posts: 2,812
Raccoon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,812
Interesting, I hadn't known about that.

However, I would still like to see this method put into place. Assuming it doesn't involve a lot of coding to implement, it would be a huge time-saver, and wouldn't require switching everything from /var <%variable> = <value> to /set -u <%variable> <%value> when it's decided that a block of code would be better represented in a sub-routine.


Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Joined: Dec 2002
Posts: 344
D
Pan-dimensional mouse
Offline
Pan-dimensional mouse
D
Joined: Dec 2002
Posts: 344
The way I like to deal with allowing values to be quickly modified in a script is to put lines like this at the top:

Code:
alias -l bot.name { return MyGoatBot }
alias -l bot.owner { return MeMyselfI }

on *:PART:#:{ if (($me == $bot.name) && ($nick == $bot.owner)) { part $chan wait for me! } }


In any case, I think the biggest problem with adding subroutines is that there are already ways to deal with the problems that it solves. I don't think the benefits are really worthwhile compared to the added complexity required.

Joined: Oct 2004
Posts: 8,330
Hoopy frood
Offline
Hoopy frood
Joined: Oct 2004
Posts: 8,330
Right. Using a return value from an alias like that is even better. I was just showing what was basically a 1 to 1 representation of what was requested. smile

To the OP: It might take a little while to make changes to scripts already written, but not that long and you'd have to make changes regardless if some other method were created. All you're change are the SUB's, which aren't hard to find or edit. Any other changes would have to be made to both the SUB section AND the rest of the script and would be done for both your way and the /set way. And, for that matter, your SUB sections would have to be written anyhow. I don't see that it would add much of any extra work.

Considering mIRC isn't likely to add anything new this version, you're going to have to wait at least 6 months or so before there would be any chance of it being added. Instead, you could just use one of the two methods given and then you can have your modular ability immediately, which means you will have fewer scripts to edit (you'll have more if you wait until something like this gets added... if it ever does). In the end, you save a lot of time using one of these methods. And once you have your current scripts updated, anything new can be done this way from the start if you plan ahead a bit.

Also, considering the way I showed you does pretty much the exact same thing other than using /set instead of /var, I really doubt this would be added just so users can use /var instead of /set. Throw in drum's example and it's even less likely. I could be wrong, but if it were me, I wouldn't wait and hope for it to happen and would just use what was already available. smile


Invision Support
#Invision on irc.irchighway.net
Joined: Sep 2005
Posts: 2,881
H
Hoopy frood
Offline
Hoopy frood
H
Joined: Sep 2005
Posts: 2,881
Yeah I'm really not sure there's any advantage to this over /set -u other than the fact that /var is more commonly used.

Not everybody wants their scripts to be expanded upon though, so having both is probably a good idea.

Joined: Jan 2004
Posts: 162
R
RRX Offline
Vogon poet
Offline
Vogon poet
R
Joined: Jan 2004
Posts: 162
I'm not sure what exactly this is about.
Modular coding of a script and configuration of a script are two rather different aspects.
The former is to organize a script's code so that it allow the scripter, and also a third-party scripter, to easily see what is where and how to edit and add stuff.
The latter is about configuration of a script by the end-user of it.
Local aliases, as drum showed, are one method of the latter aspect, namely configuration. I use them myself for small scripts and/or scripts that do not have menu's or dialogs.
However, for bigger scripts, this method would mean heaps of aliases, and the need to start using token strings as their names, that's okay for the functional aliases of a script, but for this 'return a configuration parameter', it's more of a mess which goes rather away from easy editing by third-party.

So I believe this feature request is more about the former aspect, namely the organisation of the code in a script.
If you want to write a script in such a way that another scripter (so some1 that knows what he does) can easily add his own code sections/modules for it, then this gosub method would come in as a method that allows to keep the code parts separated, so no mix of one scripter's code with another's code.

But let's consider the namespace. It was said that the locals of the calling place should be preserved. Ok, but that means that the 'sub'routine cannot alter them. This is rather contradictional with the idea of being able to 'construct' a script from code on different locations/different scripters.
In the end, the third-party guy has to know the calling code anyway, including its variable names, since that's what you get when you don't like the work involved in the method of using alias $1- parameters - described input $1 = this $2 = that etc (or command /signal + event on signal)

So I have doubts about this. If it is about code organisation-location freedom, the requested namespace is in conflict and it should be more like a kind of 'global' /goto. If it's just configuration parameters, the local aliases come in, and if there are plenty ones, you could always make one called as $constants().name and in it an if/elseif structure like if ($prop = name) { return MyGoatBot } etc. Or a goto $prop and :name | return MyGoatBot.

Last edited by RRX; 11/04/10 02:45 PM.
Joined: Feb 2003
Posts: 2,812
Raccoon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,812
Originally Posted By: RRX
If you want to write a script in such a way that another scripter (so some1 that knows what he does) can easily add his own code sections/modules for it, then this gosub method would come in as a method that allows to keep the code parts separated, so no mix of one scripter's code with another's code.

But let's consider the namespace. It was said that the locals of the calling place should be preserved. Ok, but that means that the 'sub'routine cannot alter them. This is rather contradictional with the idea of being able to 'construct' a script from code on different locations/different scripters.


I stated that locals would be 'preserved' from the caller, become available to the caller when the sub returns. That would mean alterations would be passed back to the caller.

My problem is that I tend to keep my code modularized, and I avoid global variables due to potential name collision, but it's otherwise impossible to pass two space-containing strings to an alias without using some token separator or calling the alias as an $identifier. It's also dreadfully slow to constantly pass several large strings back and forth from alias to alias.

Currently, I've been using binary variables as cross-alias storage containers, but they too are extremely inconvenient.

Anyway. Here's a small example where sub routines would come in handy.

http://www.hawkee.com/snippet/7339/

Imagine if there were 50 variables available to the user instead of just the 1. As is the case with a socket custom chat protocol interface script I'm currently writing.


Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Joined: Oct 2004
Posts: 8,330
Hoopy frood
Offline
Hoopy frood
Joined: Oct 2004
Posts: 8,330
You do know that $chan and $nick are inherited by the alias automatically, right? You don't have to pass them to the alias.

Code:
on *:part:#: {
  somealias
}

alias somealias {
  echo -a $nick parted $chan
}


Invision Support
#Invision on irc.irchighway.net
Joined: Jul 2008
Posts: 236
S
Fjord artisan
Offline
Fjord artisan
S
Joined: Jul 2008
Posts: 236
I believe you've misunderstood the normativeness of the "sub" and "byref" keywords. If you've taken them from VB, you've chosen two completely unrelated terms to describe this value. They have little, if not nothing to do with static variables or scope. I don't see how this would "allow greater ability for modular coding while not being too outlandish in expectation". Greater than what? Use hashtables for your configuration, then you'll find that the ability for modular coding is increase beyond the scope of your suggestion.

mIRC has a primitive form of subroutine: aliases. The difference is you can't specify the type (because mIRC is a weakly-typed programming language) or name of parameters or return values. I have no issue with your idea, however implementing this functionality as the inappropriately obscure keywords "sub" and "gosub" is stupid and could lead to confusion from gurus, who actually know a thing or two about programming. Perhaps your idea would be best described as a form of scope extension ... which, aside from nested functions, hasn't been done in a programming language yet, and would be a great idea. It needs one keyword. Something like "extend", not "GoSub".

As far as I know, everything in mIRC is by value (as opposed to by reference). To explain the difference it is necessary to understand how the parser passes values between functions. Using the following example I hope to explain to you what byref actually means.

Code:
alias caller {
  var %text = Hello, callee. I have your coffee for you.
  callee %text
  echo %text
}

alias callee {
  tokenize 32 Thankyou, caller. You make a great pot of coffee!
}


From the output of this piece of code, one can observe that "arguments" passed to functions are passed by value. The caller declares a variable, passes (the value of) that variable to the callee. The callee then modifies it's local version of that variable (using the only method I know of to do anything similar to this). The callee's reassignment will not affect the caller's value. %text will be the same after the call to callee, as it was before the call to callee. If the variable was passed by reference, %text's value would have changed when callee reassigned it's parameter storage space.

Now, by adding a timer to this example, I hope to explain to you why it would be difficult to implement passing by reference (ByRef!):
Code:
alias caller {
  var %text = Hello, callee. I have your coffee for you.
  .timerGiveCoffee 1 0 callee %text | echo %text
  .timerGiveCoffee
}

alias callee {
  tokenize 32 Thankyou, caller. You make a great pot of coffee!
}


From the output caused by the call to the alias timerGiveCoffee without any arguments, one could note that mIRC appears to simply substitute the value of %text into the expression "callee %text | echo %text". This leads to a difficulty in tracking the argument. The variable appears to be removed from evaluation after declaring the timer, so how is callee to know that it's input originated from a variable to begin with?

Joined: Feb 2003
Posts: 2,812
Raccoon Offline OP
Hoopy frood
OP Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,812
Sorry. I've been programming for 20+ years, but only dabbled briefly in VB5 for a year or two... so I really don't understand anything you just said.

Perhaps VB's usage of these terms differs from the norm, but I was in fact paraphrasing when I used the word "ByRef", hence the paraphrases and exclamation mark. My suggestion is the closest thing to "ByRef" that mIRC would be capable of -- scope expansion as you call it.

As far as Sub-routines go; Aliases ARE sub-routines with the bonus that they can behave like functions if you pass parameters to it. You don't pass parameters to sub-routines (ie, GoTo in most languages) and variables from the caller ARE accessible to the called.

My suggestion is simply a lower level GoTo/Label by a different name, using existing addressing schemes.


Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
This seems more like macros to me, or inline functions. I'm sure people from various paradigms will interject their own ways of doing it...

However, I don't really think there's any real need for mIRC to directly support a new syntax for this kind of idiom. There are already ways, as people pointed out, to get the same effect with virtually the same lines of code.

*NOT* relying on locals, for one thing, would be a good start. mIRC has a limited concept of scope. However, even if it only understands "global" and "local" (and "local-to-script" for aliases), there's no reason to change this.

In your example you use /var's to specify configuration data, but the example does not seem realistic, as I don't see any reason to make that data local. You could easily set the bot name and owner as globals, as even you call them "CONSTANTS". As constants, they should be global. If global to all scripts is a problem, mIRC has /alias -l to create "CONSTANT" identifiers.

alias -l bot.name { return NAME }
alias -l bot.owner { return OWNER }

This is far more in line with what you wanted anyway, because they're actually "constant" in this case, unlike variables. I see no functional difference with this syntax, just aesthetic. However, you do get the aesthetic benefit of *not* having to call "gosub MYROUTINE" at the start of every event, since now you have access to those identifiers without asking. You're actually using less code to express it this way. That, IMO, is a win for the existing syntax over any gosub/macro concept.

I know you admitted your example is weak, but I think that's telling of the fact that there really is no "good example" to illustrate the need of such functionality. Do you have a more realistic example where there really is no 1:1 equivalent using the existing syntax to solve the problem? By 1:1 I mean in rough line of code count. If not, I just don't see any benefit besides yet another way to do the same thing-- and IMO mIRC has enough of those.


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"

Link Copied to Clipboard