Map shorthand

Discuss the future of the AutoHotkey language
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: Map shorthand

Post by TheArkive » 24 Apr 2021, 02:03

Map(key, value, key2, value2, ...) works for me.

One down side is that if you want case insensitive keys then you must:

Code: Select all

obj := Map()
obj.CaseSense := false
obj[key] := value
obj[key2] := value2
...
SInce Map() is now a class, it should be extendable... I'll see what I can come up with...

lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Map shorthand

Post by lexikos » 24 Apr 2021, 02:52

You "must" assign the values individually?

It's trivial to write a function or class that uses the same syntax as Map, with case-insensitivity and a different name.

At worst, make the function variadic and use a loop.

Code: Select all

Mip(p*) {
    m := Map()
    m.CaseSense := false
    loop p.Length//2
        m[p[A_Index*2-1]] := p[A_Index*2]
    return m
}

MsgBox Mip('A', 'b')['a'] ; b
Or you could just use Set.

Code: Select all

Mip(p*) {
    m := Map()
    m.CaseSense := false
    return m.Set(p*)
}

Code: Select all

Mip(p*) => (m := Map(), m.CaseSense := false, m.Set(p*))

MsgBox Mip('A', 'b')['a'] ; b
Or you can set CaseSense during initialization (which is before the items are added by the constructor).

Code: Select all

class Mip extends Map {
    CaseSense := false
}
MsgBox Mip('A', 'b')['a'] ; b
__New is an alias of Set, and can be used in the same manner; but don't.

Note the post dates prior to iseahound's post.

User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Map shorthand

Post by vvhitevvizard » 24 Apr 2021, 05:35

lexikos wrote:
24 Apr 2021, 02:52
m.CaseSense := false
BTW, I suppose CaseSense's "On"/"Off" needs to be addressed:
https://lexikos.github.io/v2/docs/objects/Map.htm#Set
Because u unified it and removed literal strings for such boolean switches overall.

User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: Map shorthand

Post by TheArkive » 24 Apr 2021, 05:59

Wow, i was way off. I need to remember to keep going back to read the docs... :facepalm:

I had forgotten about Map.Set().

lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Map shorthand

Post by lexikos » 24 Apr 2021, 18:25

vvhitevvizard wrote:
24 Apr 2021, 05:35
BTW, I suppose CaseSense's "On"/"Off" needs to be addressed ... Because u unified it and removed literal strings for such boolean switches overall.
Nope.

CaseSense is not boolean; it returns one of three string values:
"On"
"Off"
"Locale"

These are the same values accepted by StrReplace, StrCompare, InStr, IsAlpha, IsAlnum, IsUpper and IsLower.

Strings are used because they are clearer, especially for "Locale" and "Logical" (which is accepted by StrCompare).

Integer pseudo-boolean values are not preferred because if 0 is not case sensitive, if CaseSense would logically be used to check for case-sensitivity, but would actually only be checking if CaseSense is not the non-locale case-insensitive mode. In other words, it would mistake "Locale" as case-sensitive.

Reversing the meaning of the property and various parameters would permit if CaseInsensitive where CaseInsensitive could be true or 2 or "Locale", but it doesn't seem worthwhile.

Hotkey, Hotstring, SetCapsLockState and similar functions also accept "On" and "Off". Hotkey does not accept 1 or 0 in their place.

User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Map shorthand

Post by vvhitevvizard » 25 Apr 2021, 07:35

lexikos wrote:
24 Apr 2021, 18:25
CaseSense is not boolean; it returns one of three string values:
"On"
"Off"
"Locale"
I'm aware it has the 3rd option thus making it not suitable for binary logic.
Just a proposition, for CaseSense, it might be just another boolean property added, e.g. m.CaseSense true/false for sensitive/insensitive and m.Locale true/false as an additional mode for case insensitivity. And the very same logic of splitting a literal string value into 2 boolean switches for other mixed switches remaining (e.g. Critical ["On"|"Off"|Numeric]). That would unify the overall pattern, improve performance (pure numbers vs strings) and dramatically alleviate filling these switches programmatically.

lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Map shorthand

Post by lexikos » 25 Apr 2021, 19:55

There are exactly three possible modes of comparison, not two separate binary options. "Locale" and "case-sensitive" are mutually exclusive, and the setting can't be changed once you add any items. I see no benefit to having two separate properties for it, only drawbacks.
That would unify the overall pattern,
Would you change the several functions I mentioned as well, splitting their CaseSense parameter into two parameters? If not, I suppose that's the opposite of unification. Even if things are "more unified", specifically how is it helpful?
and dramatically alleviate filling these switches programmatically.
How so? Perhaps you can demonstrate.

Critical/A_IsCritical already accepts and returns a pure integer, and there's no need to save or restore two separate settings.

As for performance, I don't believe this is a real concern in general. For properties specifically, assigning one string property is most likely faster than assigning two integer properties.

iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Map shorthand

Post by iseahound » 25 Apr 2021, 21:27

Has there been any further consideration of named parameters? I read in your other post that there would be no further big changes to the language.

Currently unpacking only extends to keys:

Code: Select all

a := Map("a", "AAPL", "b", "BA", "c", "CHWY")

fn(a*)

fn(p*) {
   for i, v in p
      MsgBox i ", " v
}
I was thinking of behavior similar to **kwargs in Python.

iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Map shorthand

Post by iseahound » 07 Jun 2021, 11:22

I think that it needs to be decided if {} will refer exclusively to AutoHotkey objects or maps. I don't really have a preference, but whatever the choice is will likely stick. As finc's post says, I do find myself using object properties as a return value instead of a map. Given expectations of JSON objects and python dicts, it may be adventurous to do otherwise.


Upon thinking about it, I believe buliasz 1st suggestion to be the most preferable.

Code: Select all

map := [
	"key1" : "value1",
	"key2" : "value2",
	...
]
Part of the reason why is because both arrays and maps are ordered so maintaining the same syntax feels like a natural extension of the other.

If linked lists/ unordered dictionaries, hash tables (also known as python sets) ever get implemented they should use an alternative syntax.

lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Map shorthand

Post by lexikos » 11 Jun 2021, 20:09

1. It was decided before this topic even started. :roll:
2. That syntax was suggested by Helgef in the second post of this topic, and I already brought up one problem with it.
3. Your reasoning that both arrays and maps are ordered is flawed. An ordered map would retain the order in which the key-value pairs are specified, like an Array. That is not how Map works.

iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Map shorthand

Post by iseahound » 12 Jun 2021, 10:48

3. Sorry, I meant sorted. Currently Maps and Arrays are auto sorted.
2. Is map := [:] unreasonable?
Square brackets are the least offensive choice, and perhaps that may not be reason enough.

lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Map shorthand

Post by lexikos » 12 Jun 2021, 17:26

Arrays are not auto sorted. The order of items in an Array are retained. They are stored in that exact order, and accessed by calculating the offset based on the index, not by searching. The order of items in a Map are not retained. Maps and Arrays are not equivalent in either regard.

I already posted the idea of [:] last year. There's no dedicated syntax for Map not because I couldn't decide on syntax, but because I decided it was unnecessary.

User avatar
RaptorX
Posts: 371
Joined: 06 Dec 2014, 14:27
Contact:

Re: Map shorthand

Post by RaptorX » 10 Apr 2022, 12:42

The only thing that really annoys me is the , separated nature of the current call, which makes my brain hurt sometimes because a "function call"-like syntax (yeah, I know is not a function call, but it looks like one) is not really indicative of association between the parameters to be honest.

I do understand that we could easily do this:

Code: Select all

api.headers := Map("Content-Type", "application/x-www-form-urlencoded",
	                   "WWW-Authenticate","Basic")
Which looks ugly as hell and takes up too much space if you start separating key-value pairs to their own lines.

But the main issue is that I have been using ad-hoc objects in v1 for so long that I always try:

Code: Select all

api.headers := {"Content-Type": "application/x-www-form-urlencoded", "WWW-Authenticate":"Basic"}
before getting yelled at because I cant have quotes around properties...

If I could modify what I just wrote a tiny bit instead of having to re-write the whole thing to change the braces, and change all :'s to ,'s would be great.

So probably just needing to this:

Code: Select all

; any of these modifications would be appreciated as there is less to type/modify after
; I foolishly wrote all my ad-hoc object  in the old notation.
api.headers := ["Content-Type": "application/x-www-form-urlencoded", "WWW-Authenticate":"Basic"]
api.headers := ${"Content-Type": "application/x-www-form-urlencoded", "WWW-Authenticate":"Basic"}
api.headers := map{"Content-Type": "application/x-www-form-urlencoded", "WWW-Authenticate":"Basic"}
without optionally having to create a custom class on each new script would be great.

Note: these examples seem trivial as there is not much data, but some times I write or copy/paste many key-value pairs (from JSON or javascript) before noticing and then having to use find/replace and deal with unwanted replacements when a simple map,$,[:] would suffice.

Usually the issue is that I have a bunch of key-value pairs that happen contain characters that cant be used in a property name.
That forces me to use a map even if I think of them as properties.

And probably you guys don't see those cases very often which is maybe one of the reasons why @Lexicos probably decided not to implement a shorthand, but I believe that it would be a valuable addition to the language in general.

Everything that makes the programmer's intention clear is a good thing, and a "function call"-like statement does not convey the associative nature of a map.

I do understand that exception in the context of an array, as there is no association between items, except for their relative positioning to one another which can be clearly seen in oArray := array("one", "two", "three"), but to drive my point home, I never use object("prop", "value") notation for the same reason stated above, {prop: value} better conveys what I am trying to do and for that reason I simply do not use object() syntax when Im doing simple objects.
Projects:
AHK-ToolKit

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Map shorthand

Post by Helgef » 11 Apr 2022, 03:48

Everything that makes the programmer's intention clear is a good thing, and a "function call"-like statement does not convey the associative nature of a map.
I think these are very good points :thumbsup:
I do understand that we could easily do this:

Code: Select all

api.headers := Map("Content-Type", "application/x-www-form-urlencoded",
	                   "WWW-Authenticate","Basic")
That is close to what I do,

Code: Select all

api.headers := Map(
	"Content-Type", "application/x-www-form-urlencoded",
	"WWW-Authenticate", "Basic"
)
I think it good enough.

Cheers.

User avatar
RaptorX
Posts: 371
Joined: 06 Dec 2014, 14:27
Contact:

Re: Map shorthand

Post by RaptorX » 11 Apr 2022, 19:41

Helgef wrote:
11 Apr 2022, 03:48

Code: Select all

api.headers := Map(
	"Content-Type", "application/x-www-form-urlencoded",
	"WWW-Authenticate", "Basic"
)
I think it good enough.

Cheers.
Oh yeah dont get me wrong, i also do that... the annoyance comes when you are trying to convert a JSON object that you copy pasted or an existing v1 code to v2.

When you have this:

Code: Select all

headers := { "Host"                     : "www.facebook.com"
           , "Connection"               : "keep-alive"
           , "Origin"                   : "https://www.facebook.com"
           , "Upgrade-Insecure-Requests": "1"
           , "Content-Type"             : "application/x-www-form-urlencoded"
           , "User-Agent"               : "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
           , "Accept"                   : "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*"
           , "Sec-Fetch-Mode"           : "navigate"
           , "Sec-Fetch-User"           : "?1"
           , "Sec-Fetch-Dest"           : "document"
           , "Referer"                  : "https://www.facebook.com/groups/agroup/member-requests"
           , "Cache-Control"            : "max-age=0"
           , "Cookie"                   : cookie1
           , "DNT"                      : "1"
           , "Sec-Fetch-Site"           : "same-origin"}
There's a lot of room for errors because there are many little steps involved (with some find/replace that usually goes wrong)... switch braces to parenthesis, colons for commas and stuff like that.

If there is a short hand the ONLY change needed would be this:

Code: Select all

headers := map{ "Host"                     : "www.facebook.com"
; ...
}
That would be great, wouldnt you agree?
Projects:
AHK-ToolKit

megavlad
Posts: 26
Joined: 01 Mar 2018, 00:42

Re: Map shorthand

Post by megavlad » 03 May 2022, 03:05

I'm gonna have to agree with the panel and disagree with Mr lexikos.

I think having dedicated syntax for Maps literals, in a modern language, is a border-line necessity. Maps are 1 of the 2 most important Collection types, with the other being Arrays. I was very pleased when I found out that AutoHotkey supports array literals.

In my programming career, in all kinds of teams, maps have always seen a ton of use. The better the syntax, the more pleasant the experience, while the inverse is also true: ugly or missing map-literal syntax makes a sad panda.

The trend in programming language seems to be settling on {} as maps.

Code: Select all

Python: {"key": value}
Lua tables: {key = value}
Powershell: @{ key = value }
JavaScript: {key: value}
Json: {"key": "value"}
Dart: {key:value}
go: map[string]T {"key": T}
"Technically", {} is an object in JavaScript. But everyone and their moms use them as maps.

Lexicos, as others have requested, I hope you'll consider using the { } for map literals, and migrating the object literal to another syntax. I suspect that most of us will be using map-literals much more than object-literals.

IMO, this syntax would be ideal for maps:

Code: Select all

{
 "key1": 42,
 "key2": "hello",
 "key3": {
     "innerKey": 3.14
  }
}
Perhaps something like this would work for object literals (borrowing from Powershell):

Code: Select all

@{
  key1: value1,
  key2: value2
}
Regards

lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Map shorthand

Post by lexikos » 03 May 2022, 03:50

I hope you'll consider using the { } for map literals, and migrating the object literal to another syntax.
I think you mean reconsider, and no. It's too late, and I would stand by my original choice anyway.
v2.0 beta 1 was released in July 2021. Changes prior to the final v2.0.0 release should be of a minor nature (such as improvements to the error and warning dialogs), and should not affect the functionality of scripts. In other words, future releases are expected to be backward-compatible.
Source: AutoHotkey v2
Don't expect v3 (for real) any time soon.

iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Map shorthand

Post by iseahound » 03 May 2022, 11:05

What about m := Map({key: value}) ? This doesn't add any new syntax, just converts from an object into a dictionary. It would be useful to have such a conversion, (perhaps for serialization?), regardless. It answers the main issue of the map shorthand question: copying a JSON object into AHK with minimal fuss.

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Map shorthand

Post by Helgef » 03 May 2022, 12:19

It is trivial to write mymap({...}), but there are limitation due to props being stored as strings.
Don't expect v3 (for real) any time soon.
:cry:

User avatar
RaptorX
Posts: 371
Joined: 06 Dec 2014, 14:27
Contact:

Re: Map shorthand

Post by RaptorX » 03 May 2022, 19:51

iseahound wrote:
03 May 2022, 11:05
What about m := Map({key: value}) ? This doesn't add any new syntax, just converts from an object into a dictionary. It would be useful to have such a conversion, (perhaps for serialization?), regardless. It answers the main issue of the map shorthand question: copying a JSON object into AHK with minimal fuss.
I would definitely overcome my annoyance at how ugly it might look and use it for the convenience of copying JSON strings from anywhere on the web and having a native AHK object out of it.

It also solves the hassle of looking for a decent JSON parser/serializer library that wont force me to do this nonsense:

Code: Select all

json := load(string)
value := json["key"]["anotherkey"]["someadditionalkey"]
EDIT:
Helgef wrote:
03 May 2022, 12:19
It is trivial to write mymap({...}), but there are limitation due to props being stored as strings.
Don't expect v3 (for real) any time soon.
:cry:
I think thats where the problem might arise, handling arrays and keys in a logical way.

Most guys who tried creating a library for it decided not to spend the time to do the following:
  • any reserved keyword must be converted to a map key
  • any key with spaces/invalid chars should be a map key
  • any key that would be a valid property should (in my opinion) be a property

maps can handle properties and keys, not sure why they dont like using the full potential of a map.

And also not sure why map isn't the default object since it has more "capabilities" compared with a "plain object". :P
Projects:
AHK-ToolKit

Post Reply

Return to “AutoHotkey Development”