{"id":13721645,"url":"https://github.com/technicallyagd/unpack","last_synced_at":"2025-05-07T14:30:37.128Z","repository":{"id":50949756,"uuid":"160368698","full_name":"technicallyagd/unpack","owner":"technicallyagd","description":"sequence/object unpacking/destructuring for nim","archived":false,"fork":false,"pushed_at":"2019-03-05T03:10:17.000Z","size":41,"stargazers_count":55,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-11-14T11:39:38.807Z","etag":null,"topics":["destructuring-assignment","javascript","nim","python","unpacking-sequences"],"latest_commit_sha":null,"homepage":null,"language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/technicallyagd.png","metadata":{"files":{"readme":"readme.md","changelog":"changeLog.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-04T14:18:25.000Z","updated_at":"2024-10-12T23:14:46.000Z","dependencies_parsed_at":"2022-09-07T15:40:30.078Z","dependency_job_id":null,"html_url":"https://github.com/technicallyagd/unpack","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technicallyagd%2Funpack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technicallyagd%2Funpack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technicallyagd%2Funpack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technicallyagd%2Funpack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/technicallyagd","download_url":"https://codeload.github.com/technicallyagd/unpack/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252895472,"owners_count":21821167,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["destructuring-assignment","javascript","nim","python","unpacking-sequences"],"created_at":"2024-08-03T01:01:19.710Z","updated_at":"2025-05-07T14:30:36.815Z","avatar_url":"https://github.com/technicallyagd.png","language":"Nim","readme":"# Unpack\n\nArray/Sequence/Object destructuring/unpacking macros attempt to quench the thirst of (Python, ES6, et al.)-spoiled people.\n\n## Installation\n\n```cli\nnimble install unpack\n```\n\n## Example Usage\n\n```nim\nimport unpack\n\nlet someSeq = @[1, 1, 2, 3, 5]\nsomeSeq.unpackSeq(a, b, c) # creates a, b, c with 'let'\n# is expanded into:\n# let\n#   a = someSeq[0]\n#   b = someSeq[1]\n#   c = someSeq[2]\n\n# or equivalently:\n[a2, b2, c2] \u003c- someSeq\n\nsomeSeq.unpackSeq(a3, _, _, b3) # use `_` to skip index\n# is expanded into:\n# let\n#   a3 = someSeq[0]\n#   b3 = someSeq[3]\n\necho a, b, c                  # 112\nsomeSeq.unpackSeq(var d, e) # creates d,e with 'var'\n\n# or equivalently:\n[var d2, e2] \u003c- someSeq\n\nsomeSeq.aUnpackSeq(d2, e2) # assigns someSeq[0] to d2, someSeq[1] to e2\n\n# or equivalently:\n[d2, e2] \u003c-- someSeq\n# yes, \u003c-- for assignment; \u003c- for definitions.\n# This is not a typo.\n\ntype\n  Person = object\n    name, job: string\n\nlet tim = Person(name: \"Tim\", job: \"Fluffer\")\n\n# create name, job with let and assign respective member values to them\ntim.unpackObject(name, job)\n\n# or equivalently:\n# {name, job} \u003c- tim\n\n# is expanded into:\n# let\n#   name = tim.name\n#   job = tim.job\n\n# you can also unpack into custom names using 'as'\n{job as someJob, name as otherName} \u003c- tim\n\n# or equivalently:\ntim.unpackObject(job as someOtherJob, name as someOtherName)\n\n# is expanded into:\n# let\n#   someOtherName = tim.name\n#   someOtherJob = tim.job\n\nvar\n  secreteState, arg = 0\nproc someProcWithSideEffects(person: Person, input: int): Person =\n  secreteState += 1\n  {var job, name as newName} \u003c- person\n  newName \u0026= $input\n  result = Person(name: newName, job: job)\n\n# using this at the end of proc chain will not invoke proc chain multiple times\ntim.someProcWithSideEffects(arg).unpackObject(name as tim0, job as job0)\n\n# or equivalently:\n# {name as tim0, job as job0} \u003c- tim.someProcWithSideEffects(arg)\n\n# is expanded into:\n# let someUniqueSym1_212_498 = tim.someProcWithSideEffects(arg)\n# let\n#   tim0 = someUniqueSym1_212_498.name\n#   job0 = someUniqueSym1_212_498.job\n\n# if you haven't noticed,\n# this means we can unpack named tuples like objects\ntype\n  SomeTuple = tuple[x, y, z, i, j, k: int; l, m: string]\n\nlet someTuple = (1, 3, 7, 0, 3, 6, \"so\", \"lengthy\").SomeTuple\n\n# with vanilla nim, to get arbitrary fields\nlet (_, diz, _, iz, _, _, _, it) = someTuple\n# it gets lengthy\n\n# with this package\n{y as diz2, i as iz2, m as it2} \u003c- someTuple\n# Mind. Blown.\n\n# also, if you only care about the first three items\n[nice, n, sweet] \u003c- someTuple\n\n# with vanilla nim\nlet (youNeedTo, writeSoMany, underscoresMan, _, _, _, _, _) = someTuple\n\n# also supports nested unpacking\nlet nestedTuple = ((123, 321), (-3, (1, 2, 3, 4)))\n\n# This nesting is unnecessarily deep, but it's supported.\n[ [run, outOf], [names, [_, _, toUse, now]]] \u003c- nestedTuple\n\n# to be continued...\n\n```\n\nSee [tests/theTest.nim](tests/theTest.nim) for more usages.\n\n## Provisional Features\n\nFollowing features are implemented but the syntax or their actual effects are still in question.\n\n### Rest operator for unpacking sequences\n\nLike in Python (`*a,b = range(5)`) and modern JavaScript `let [a,...b] = someArray`, you can put the rest of the sequence into a new sequence. I haven't decided on the actual prefix to use yet, but I am settling on `*` as used in Python for now. If you have better ideas, please start an issue to discuss other options.\n\n```nim\n\nimport unpack\n\nlet mamaHen = @[3, 4, 5, 6, 7]\n\n[a, b, *sneakyFox] \u003c- mamaHen\n\n# is expanded into:\n# let\n#   a = mamaHen[0]\n#   b = mamaHen[1]\n#   sneakyFox = mamaHen[2..^1]\n\nassert(sneakyFox == @[5, 6, 7])\n\n[*sloppySavior, e] \u003c- sneakyFox\n\nassert(sloppySavior == @[5, 6])\n\n# Perhaps the variable naming may be a bit mis-leading,\n# since mamaHen[x..y] creates a new sequence and copy the slice into it,\n# so rather than stealing, the sneakyFox actually cloned(?) whatever mamaHen had with her\n\n# You can use *_ to skip the beginning\n[*_, pickyFox] \u003c- mamaHen\n\nassert(pickyFox == 7)\n\n# It's okay to take the middle chunk too.\n[f, g, *randomFox, _, h] \u003c- mamaHen\n\nassert([f, g, h] == [3, 4, 7])\nassert(randomFox == @[5])\n\n# Due to restriction from nim's grammar, `*` following `var`\n# is not allowed. Adding `_ as` before it is the current hack I chose to bypass this.\n[var _ as *boldFox, i, j] \u003c- mamaHen\n\nassert([i, j] == [6, 7])\nassert(boldFox == @[3, 4, 5])\n\n# They are indeed created with var.\ni = 12\nboldFox[2] = 123\n\nassert(i == 12)\nassert(boldFox == @[3, 123, 5])\n\n```\n\nUnder the hood, `unpack` just attaches `[countFromStart..^countFromEnd]` to whatever you throw at it, so anything that has slice operator implemented should work. Which also brings us to our first caveat.\n\n#### Caveat\n\n##### Doesn't Work on tuples\n\nUnless you implement the `..` operator (and its friends) yourself though.\n\n##### Only one rest operator per unpacked sequence\n\n`[*a, *b, c] \u003c- someSeq` is not allowed. It might be possible, but I think it will be really messy (plus I am lazy). Same restriction applies to both Python and JavaScript, so I think it's okay to skip this part for now.\n\nHowever, using rest operator in different parts of the nested sequence is fine, since they have different index counters, so this will work:\n\n```nim\n[[*a, b], [c, d, *e], *f, g, h] \u003c- someNestedSeq\n# is expanded into:\n# let\n#   b = someNestedSeq[0][^1]\n#   a = someNestedSeq[0][0..^2]\n#   c = someNestedSeq[1][0]\n#   d = someNestedSeq[1][1]\n#   e = someNestedSeq[1][2..^1]\n#   h = someNestedSeq[^1]\n#   g = someNestedSeq[^2]\n#   f = someNestedSeq[2..^3]\n```\n\n##### Can't guard against incorrect index access at compile time\n\nSince we have no way to know the sequence length at compile time, (well, at least I don't know a way). We can't know if you are trying to do something goofy like:\n\n```nim\n[a, b, *c, d, e] \u003c- @[1,2,3]\n```\n\n## Notes\n\n### About the syntax\n\n#### Using `let` in [] and {} is not allowed\n\nYes, I also wanted to have the natural `[let x, y] \u003c- someSeq` syntax for defining new symbol with let, `[x, y] \u003c- someSeq` for assignment, but the compiler deems it illegal. I ended up settle with more verbose assignment syntax since I anticipate it being used less often.\n\n## TODO\n\n- Docs\n- Maybe we can also support tables?\n- More informative error message for out of bound sequence access during unpacking.\n\n## Maybe TODO\n\n- rest operator for objects/tables.\n\n## Suggestions and PR's are welcome\n\nEspecially if you know how to make this macro easier to use. Also, if you know any other existing package that does this kind of stuff better, please let me know, thank you.\n","funding_links":[],"categories":["Macros","Language Features"],"sub_categories":["Byte Size","Macros"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechnicallyagd%2Funpack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftechnicallyagd%2Funpack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechnicallyagd%2Funpack/lists"}