Nothing.
Re: Nothing.
I'll just clarify my point more formally (or to confuse people further?)
If the K-complexity of the function that changes the array indexes is equal to the function itself, then it should be an UnsetError.
So here the k-complexity is the kolmogorov complexity or the smallest function that can produce the intended array. In other words, a normal function might restrict its values from 1-100 and that initial guard or check would be a ValueError. This if statement that guards access to the rest of the function can be separated from the actual function itself. However checking the length of an array can't really be a guard since the length of the array is related to the algorithmic complexity of the actual function. Since checking the index / length of the function would be equivalent to just modeling the function itself, it acts the same way as a key to a dictionary/map would.
So after writing this all out, I suppose the easiest way to explain it is to say: If you can separate the allowable types from the function then throw a TypeError. If you can separate the allowed parameters throw a ValueError. But if a separation between input and allowable values isn't possible because the two are codependent, then you're not really guarding anything! You are just restricting access to the function.
If the K-complexity of the function that changes the array indexes is equal to the function itself, then it should be an UnsetError.
So here the k-complexity is the kolmogorov complexity or the smallest function that can produce the intended array. In other words, a normal function might restrict its values from 1-100 and that initial guard or check would be a ValueError. This if statement that guards access to the rest of the function can be separated from the actual function itself. However checking the length of an array can't really be a guard since the length of the array is related to the algorithmic complexity of the actual function. Since checking the index / length of the function would be equivalent to just modeling the function itself, it acts the same way as a key to a dictionary/map would.
So after writing this all out, I suppose the easiest way to explain it is to say: If you can separate the allowable types from the function then throw a TypeError. If you can separate the allowed parameters throw a ValueError. But if a separation between input and allowable values isn't possible because the two are codependent, then you're not really guarding anything! You are just restricting access to the function.
Re: Nothing.
I'm only a novice, and have no formal skills nor training in any IT adjacent field. But I can imagine a scenario why I would want to have an empty spot in an array that gets passed over when being enumerated. Say I'm using an array of arrays to represent a table structure, where each index in the row vector is associated with a column header at index 0. The position of each item is now significant.Descolada wrote: ↑12 Sep 2024, 04:02
I disagree that null/unset passed to a function should produce no change. Such code should throw an error, otherwise it'd lead to hard-to-debug problems in the future (as AHK v1 did with silent fails). Either your array must have values at all indices, or your function should be able to handle unset, pick one. What is the objective of leaving an empty hole in an array anyway?
Another example would be in the case of a large dataset that uses relative position to some degree of significance. For example, defining an improved search/lookup algorithm for large data. When traversing the dataset, and constructing a new dataset, you leave empty space between set values to account for future additions. Like, sorting large data alphabetically. If you built your new dataset using a linear approach, going back and adding an unexpected "B" item when you're already at your 1000000th item and in the "S"s is going to take a hot minute.
An array was a very bad choice of example for me to use to illustrate my preference. My use case for `null` is about named variables/properties; arrays are explicitly used when names are unneeded or unknown.
I do agree that having the possibility of the program to throw an error when referencing a null value is a good thing. But if my program were to benefit from no errors being thrown, I'd like to be able to turn the hypothetical feature off.
My `null` class will throw an error if an operation is performed upon it a variable that references the class. In the way I plan to use it, this is actually a good thing and fits the use case well. The purpose of `null` is only to allow an unset property to exist in the namespace of my code so I can use it as a reference when validating user input. My other options were to copy-paste the property names into a separate object, which gets a big no from me. Or to use a `0` or `false` value, which is fine, not a big deal, would cause literally zero issues, but its not what I want I want significant values to have significance! False is a pretty big deal, it's 50% of all possibilities! Can't be throwing around big names like that all willy-nilly. And zero is the most unique number in all the numbers. Nothing else is quite like zero, so surely we should save it for cases when zero is needed.
In my mind, `null` is the preferred name for an unset value. I also like `unset` and I use it extensively in my AHK code. But it's just not my ideal. No shade cast to the hard work done by people much more knowledgeable and talented than me - I have been having a blast learning with AHK
@Descolada thank you for the shared code I appreciate the consideration`
Re: Nothing.
what does Unset solve other than save some MB? i'd rather save my time than saving less than 1MB if it wasn't 1kb in 16GB Ram minimum requirements era.
the funny thing that you MUST double "IF" checker if used variable with unset in class; one for "hasownproperty" and another If for your thing.
which is just waste of time. id rather learn soldinrg my ram if I need to upgrade it if i was that poor at least more fun than spam writing same words , I don't use C++ here , I want use script that can be written fast and edited fast .
also you can do this with null
when with unset you cant do it and you need use if isset first, witch you must add it unless you want break your app.
the funny thing that you MUST double "IF" checker if used variable with unset in class; one for "hasownproperty" and another If for your thing.
which is just waste of time. id rather learn soldinrg my ram if I need to upgrade it if i was that poor at least more fun than spam writing same words , I don't use C++ here , I want use script that can be written fast and edited fast .
Never , Null is like a third option "user never set the value kind" and take some size on ram wont make the code crash, unset is just destroying the variable.Cebolla wrote: ↑13 Sep 2024, 01:56
In my mind, `null` is the preferred name for an unset value. I also like `unset` and I use it extensively in my AHK code. But it's just not my ideal. No shade cast to the hard work done by people much more knowledgeable and talented than me - I have been having a blast learning with AHK
also you can do this with null
Code: Select all
if x == 1{}
else if x ==2{}
else if == null{}
Re: Nothing.
I believe I undertand your meaning and yes I agree. I want to be able to reference a symbol in my code even if that symbol does not have a value, and not have the interpreter yell at me.Never , Null is like a third option "user never set the value kind" and take some size on ram wont make the code crash, unset is just destroying the variable.
I definitely get your feeling that first checking `IsSet`, then immediately after checking the value of the thing same thing was a bit annoying to get used to, and took me a while to understand why my code kept yelling at me. But after getting used to it, I now feel that the clarity and intentfulness gained from this usage is worth the slight bit of more keystrokes I have to endure to complete the task.when with unset you cant do it and you need use if isset first, witch you must add it unless you want break your app.
Re: Nothing.
These patterns can be reduced with v2.1-alpha.Cebolla wrote: ↑12 Sep 2024, 01:53Code: Select all
interval := (startImmediately_or_input.HasOwnProp('interval') ? startImmediately_or_input.interval : interval)
Code: Select all
input := (IsSet(input) ? input : AlignKeyValPairs.default['input'])
Code: Select all
interval := startImmediately_or_input.interval ?? interval
Code: Select all
input ??= AlignKeyValPairs.default['input']
In v2.1-alpha.2+, unset is a subset of the "unset expressions" and must be valid everywhere an unset expression is valid. Delete(variable) is fine when the deletion is unconditional, but an unset expression can consist of a long chain. For instance, a := b?.c?.d unsets a if b or b.c is unset. You can write (IsSet(b) && b.HasProp('c')) ? a := b.c.d : delete(a), but requiring the long form for such cases would significantly reduce the value of having optional chaining in the first place. If a chain needs to be split (e.g. to check some other condition), not being able to assign an intermediate maybe-unset result to a variable would make that difficult. Functions and properties can also return unset, and it can be impossible to know in advance whether a value will be returned. If you want to use the value, while also permitting there to be none, you must assign to a variable (e.g. a := (fn()?)).
unset itself can also be used within a conditional expression, such as a := (b ? c : unset). Being required to write such expressions like (b) ? (a := c) : delete(a) is inelegant and burdensome.
A function cannot operate on a variable without explicitly being given a reference. To work as Delete(variable) rather than Delete(&variable), or to work with properties or items, it would need to be a special case within the syntax, like IsSet.
Assignment is much easier to apply generally to variables, properties and items; simpler to implement, no new syntax to document or learn. Why do you believe an assignment must have a value? It's not that a variable can't exist without a value; variables start out in that state, and can be restored to that state.
For a property setter, := unset is actually equivalent to omitting the value parameter, just as though the setter function (or __set) was called with unset as a parameter. The parameter is mandatory if defined with the property setter syntax in v2.0, but can be made optional by defining a setter with DefineProp (or syntax in v2.1).
When you define a function, its return values have whatever meaning you give them, or no meaning if you give them none. The same would apply to a lack of return value.Now, I wonder what it would mean for a function to return "nothing". I think it'd be something like "void" in C, and it could make some sense in AHK if returning unset would turn the function into a void function.
a := b is an error if b is unset, a := b.c is an error if c is undefined or returns unset, and so a := c() is an error if c returns unset.
Returning unset is valid only in v2.1-alpha.2+. Functions default to returning "" for backward-compatibility, but this can be changed with #DefaultReturn Unset (at the moment). It does not affect built-in functions, which generally behave as in v2.0.
Returning unset doesn't "turn the function into a void function"; it just means that the subexpression which calls the function will have no value. A function can conditionally return a value or unset.
I think what you really mean is that you would prefer both the name and the general concept and behaviour to be like certain other languages which use "null". I gave it a unique name because it is not intended to be a copy of null as seen in any other language. The purpose, semantics and behaviour are different.
Re: Nothing.
Requiring it to be inelegant and burdensome can be the desired effect if the language wants to discourage unsetting variables, and I can see value in that in preventing errors.
It appears I personally never use such patterns, so perhaps I'm not the best person to question it... I searched through some of my libraries to find code patterns like a := (b ? c : unset) but found none, only had multiple uses of b ? c : unset as a function argument.
Delete(&variable) is fine, although I'm not arguing to remove the assignment syntax. Adding Delete alongside the assignment syntax would be superfluous, I'm not particularly advocating for adding that either. Being able to unset via assignment was just a tiny irk on a philosophical level, I see the practical value in it.A function cannot operate on a variable without explicitly being given a reference. To work as Delete(variable) rather than Delete(&variable), or to work with properties or items, it would need to be a special case within the syntax, like IsSet.
Wikipedia article about assignment in computer science statesWhy do you believe an assignment must have a value?
Unset/null is an absence of value, so by definition it can't be assigned.In computer programming, an assignment statement sets and/or re-sets the value stored in the storage location(s) denoted by a variable name; in other words, it copies a value into the variable.
I don't think I can see the value (no pun intended) of returning unset from a function. Could you perhaps bring an example of how/where it would be useful, or how you see users potentially using it?When you define a function, its return values have whatever meaning you give them, or no meaning if you give them none. The same would apply to a lack of return value.
Yet a := b := unset is not an error, nor is a := (b := unset). A bug?a := b is an error if b is unset, a := b.c is an error if c is undefined or returns unset, and so a := c() is an error if c returns unset.
I have a small question also. Unsetting an item eg in an array has the same effect as Array.Delete(). Unsetting a keys value in a Map has the same effect as Map.Delete(key). Unsetting an object property does not delete the property itself, but instead is passed into the setter method. Is it possible to check whether an object property is "set" without using try..catch?
Code: Select all
a.b := 1
a.b := unset
c ??= a.b ; Error: No value was returned
MsgBox IsSet(a.b) ; Error: IsSet requires a variable
MsgBox a.HasProp("b") ; 1
Class a {
static _b := unset
static b {
get => this._b ?? unset
set => this._b := value ?? unset
}
}
Re: Nothing.
Yes, that is what I really meant good call. I think your implementation of `unset` is great. It makes sense to have a different name.lexikos wrote: ↑29 Sep 2024, 02:28I think what you really mean is that you would prefer both the name and the general concept and behaviour to be like certain other languages which use "null". I gave it a unique name because it is not intended to be a copy of null as seen in any other language. The purpose, semantics and behaviour are different.
Re: Nothing.
I updated to the most recent version. Being able to conditionally return `Unset` within a property accessor, or in a function, is a work of art. Solved the only problem I had with the original implementation. Great work To Lexikos and the lead contributors: You all are doing a great job and should be proud to have played a hand in making something so cool and helpful (AutoHotkey).
Re: Nothing.
There literally is no value!Descolada wrote:I don't think I can see the value (no pun intended) of returning unset from a function.
Most of the reasons relating to unset variables and parameters apply equally to return values.
Variables defaulting to unset instead of "" improves error detection. Attempting to use a nonexistent value causes an error to be thrown, whether the value was supposed to come from a variable or from a function call. Functions don't default to returning unset yet, but one can do it explicitly for similar reasons.
Parameters defaulting to unset instead of a value allows some ambiguity to be eliminated. Compared to the alternative - making the author choose a unique default value - it improves consistency between scripts. In v1 we had built-in functions which returned an object or "" and others which returned an object or 0, but now we can have functions which return something or nothing.
The practical difference between returning unset and returning "" (or "not returning" in v2.0) is what happens when the caller attempts to use the return value. If a function (without side-effects) returns unset, the effect is the same as the caller having referenced an unset variable instead of a function call. (An UnsetError is thrown, or the expression short-circuits to ??, or a parameter is omitted if the ? suffix was used.)
No, these are valid unset expressions as explained under Unset.Descolada wrote:Yet a := b := unset is not an error, nor is a := (b := unset). A bug?
- unset is an unset expression.
- b := unset is an unset expression because it is "A direct assignment with := or ??=, where the right-hand side is an unset expression."
- a := b := unset is an unset expression because it is a direct assignment to a where the right-hand side is b := unset, which is an unset expression as per the previous point.
Unsetting a value property does delete the property itself.Descolada wrote:Unsetting an object property does not delete the property itself, but instead is passed into the setter method.
Setting a dynamic property does literally nothing except for what you instruct it to do in the setter. If you want the property to be deleted, you must delete it. Of course, you probably don't really want to do that when the property is defined in the prototype, since that's the only place you could delete it from, and that would affect all instances.
What does it mean for the b property to "be set"? A dynamic property doesn't have state; it is just a pair of methods that you can call. In this case, it is obvious that the value is stored in _b, so you can check a.HasProp("_b"). But suppose you only have a public interface like this:Descolada wrote:Is it possible to check whether an object property is "set" without using try..catch?
Code: Select all
Class a {
static b {
get => (some value or unset)
set
}
}
In general, the ?? and ? operators are the preferred constructs for dealing with unset. There are basically two ways to evaluate a potentially unset subexpression to a boolean value indicating whether a value was produced:
- Use ?? with a unique default value. This is possible even if you don't know what values the LHS might have. For instance, unique := {}, has_value := (LHS ?? unique) != unique.
- Use ? with a function which will return 0 if the parameter is unset or 1 otherwise. Knowing that IsSet does what we want but requires a variable (and isn't a function), a direct and conventional solution would be IsValue(p?) => IsSet(p), which can be called like IsValue(a.b?). [LHS?].Has(1) is creative, but less efficient.
Re: Nothing.
Descolada wrote:I don't think I can see the value (no pun intended) of returning unset from a function.
The addition of unset has become a godsend when working with databases because, despite lexikos' stated intent to differentiate, the keyword very much acts like a null value.lexikos wrote: ↑13 Dec 2024, 21:07Variables defaulting to unset instead of "" improves error detection. Attempting to use a nonexistent value causes an error to be thrown, whether the value was supposed to come from a variable or from a function call. Functions don't default to returning unset yet, but one can do it explicitly for similar reasons.
EDIT: another big use I have for unset is for conditionally setting default values, as in this method:
Code: Select all
ReadyAsync(inEasyHandles?,multi_handle?){ ;Add any number of easy_handles to the multi pool. Accepts integers or object.
inEasyHandles ??= this.easyHandleMap[0][-1] ;defaults to the last created easy_handle
multi_handle ??= this.multiHandleMap[0][-1] ;defaults to the last created multi_handle
If (Type(inEasyHandles) = "Integer")
inEasyHandles := [inEasyHandles]
for k,v in (Type(inEasyHandles)!="Object"?inEasyHandles:inEasyHandles.OwnProps()) { ;itemize Objects if required
this.AddEasyToMulti(v,multi_handle)
}
}
Last edited by Qriist on 15 Dec 2024, 12:02, edited 2 times in total.
Re: Nothing.
Haven't been able to test with the default return of unset due to bugs which I don't understand: viewtopic.php?f=14&t=131019
Re: Nothing.
Answered over there, but they seem to boil down to syntax checks that were missed when unset was added. Might be easy fixes?iseahound wrote: ↑14 Dec 2024, 19:53Haven't been able to test with the default return of unset due to bugs which I don't understand: viewtopic.php?f=14&t=131019
So, I've been chewing on this and I think I came up with something weird but workable: build your own Null type by providing a Null class.Cebolla wrote: ↑12 Sep 2024, 01:53In this above example, I am incapable of defining a value as `unset` in my template object, because that causes the property to be lost at runtime, so I will no longer be able to use my template object for one of its purposes. So in this case, `unset` does not provide for me a means of accomplishing all required tasks:
1. Exposing to the reader the possible optional input parameters
2. Allowing optional input parameters to be passed as key:value pairs in an object, instead of positional arguments
3. Preparing an effective way to validate input parameters
4. Correctly identifying an optional input parameter as not having a value unless the user/developer defines the value or a value is defined during script executing.
As explained by the docs, when you call Type() on some entity you're actually checking a class value, whether it's proper class or not. For example:
Code: Select all
randomEntity := 1
If (Type(randomEntity) = randomEntity.__Class)
&& (randomEntity.__Class = "Integer")
msgbox "The type is Integer."
Code: Select all
Class Null {
;no really, literally an empty class
}
Spoiler
I think this is probably the best way to handle Nulls in AHKv2, barring lexikos making a reserved keyword that does the same.EDIT: https://github.com/Qriist/Null
lol
Re: Nothing.
@Qriist You should be able to set a default value in the function signature. So there's no need for ? or ??= as the function reference will persist as a default value. I for get sometimes as well, mostly due to Python's strange behavior.
The null class is very clever!
The null class is very clever!
Re: Nothing.
You're absolutely right given my code's current state. What you see at the moment is a relic of an earlier design where the default was derived in a more complex manner. AHK didn't like that earlier method as a function default so I settled on the ??= checks. When I redesigned the layout to fix a different issue I didn't bother changing the function signatures. I might do so when the class is closer to completion.
Thank you! I'm glad you think so.