{"atom":"regression/keyof-capture-and-cool-subs","family":"regression","slug":"keyof-capture-and-cool-subs","header":{"atom":"regression/keyof-capture-and-cool-subs","kind":"whole-program — a minimal repro, not yet reduced to an atom","source":"curated"},"axes":null,"cols":null,"engines":["rakudo-2026.07","rakupp-3.14.0"],"kinds":["ref","pp"],"defFrom":"rakupp:t/regression/keyof-capture-and-cool-subs.raku","counts":{"a":1,"f":0,"d":0,"c":0,"r":0,"n":0},"files":["atoms/regression/keyof-capture-and-cool-subs.grid"],"records":[["0001","","# Regression: what a Hash keys on, what a Capture answers, and the Cool subs that\n# were selecting their source by VALUE TAG instead of by position.\n#   * `.keyof` never looked at the declared key type. Parser records `my %h{Int}`\n#     as declType \"Any,Int\" and `.of` already reads the value half; `.keyof` now\n#     reads the key half. A plain hash keys on the COERCION type Str(Any).\n#   * `\\(…) ~~ :(…)` asks whether the capture would BIND, which is\n#     Signature.ACCEPTS's job — the generic smartmatch had no idea what a\n#     Signature was and answered False for every capture.\n#   * `.kv` on a Capture is index/value for the positionals then name/value for\n#     the nameds; falling through to Array.kv yielded the named part as\n#     (index, Pair).\n#   * `words`/`lines` are Cool routines, so `words(42)` is \"42\".words. Selecting\n#     the source by `v.t == VT::Str` sent every other type to the `$*IN` branch,\n#     where it BLOCKED ON STDIN.\n#   * `printf($fmt, $junction)` prints once per eigenstate, in order. sprintf\n#     does NOT autothread — the junction stays one value there.\n# Contract: exit 0 + last line PASS.\nmy @fail;\nsub check($got, $want, $what) { @fail.push(\"$what: got $got want $want\") unless $got eq $want }\n\n# keyof\nmy %plain = 'apples' => 3;\ncheck(%plain.keyof.gist, '(Str(Any))', 'a plain hash keys on the coercion type');\ncheck(Hash.keyof.gist,   '(Str(Any))', 'and so does the type object');\nmy %oi{Int}; %oi{42} = 1;\ncheck(%oi.keyof.gist, '(Int)', 'an object hash keys on its declared type');\nmy %oa{Any};\ncheck(%oa.keyof.gist, '(Any)', 'whatever that type is');\nmy Int %tv{Str};\ncheck(%tv.keyof.gist, '(Str)', 'the key half is read, not the value half');\ncheck(%tv.of.gist,    '(Int)', 'and .of still reads the value half');\ncheck(Mix.keyof.gist, '(Mu)',  'an unparameterised quanthash is unaffected');\n\n# a Capture against a Signature\ncheck((so \\(7)        ~~ :($)).gist,    'True',  'one positional binds one');\ncheck((so \\(7, 8)     ~~ :($)).gist,    'False', 'two do not');\ncheck((so \\(a => 1)   ~~ :(:$a)).gist,  'True',  'a named binds a named');\ncheck((so \\(1, :a(2)) ~~ :($, :$a)).gist, 'True', 'and both together');\n# …and Signature ~~ Signature is a DIFFERENT question: is every call that binds\n# the left also bound by the right? A slurpy named does not widen the positional\n# window, so `:(*%) ~~ :()` needs more than the arity/count comparison.\ncheck((:($)    ~~ :($)).gist,   'True',  'the same signature contains itself');\ncheck((:($)    ~~ :()).gist,    'False', 'a one-arg call does not bind :()');\ncheck((:()     ~~ :($)).gist,   'False', 'nor a no-arg call bind :($)');\ncheck((:($,$)  ~~ :($)).gist,   'False', 'two positionals into one');\ncheck((:(*@)   ~~ :(*@)).gist,  'True',  'slurpy into slurpy');\ncheck((:(*@)   ~~ :()).gist,    'False', 'slurpy into nothing');\ncheck((:(*%)   ~~ :()).gist,    'False', 'a slurpy NAMED into nothing');\ncheck((:()     ~~ :(*%)).gist,  'True',  'but nothing into a slurpy named');\n\n# Capture.kv\nmy $c = \\(2, 3, apples => (red => 2));\ncheck($c.kv.gist,     '(0 2 1 3 apples red => 2)', 'kv is index/value then name/value');\ncheck($c.keys.gist,   '(0 1 apples)',              'keys');\ncheck($c.values.gist, '(2 3 red => 2)',            'values');\ncheck($c.elems,       '2',                         'elems counts positionals');\n\n# the Cool subs take the first positional whatever its type\ncheck(words(42).gist,       '(42)',    'words of an Int');\ncheck(lines(42).gist,       '(42)',    'lines of an Int');\ncheck(words('a b c').gist,  '(a b c)', 'words of a Str is unchanged');\ncheck(words('a b c', 2).gist, '(a b)', 'and still takes a limit');\ncheck(lines(\"a\\nb\").gist,   '(a b)',   'lines of a Str');\n\nif @fail { note \"FAILED: @fail.join('; ')\"; say 'FAIL' } else { say 'PASS' }","output PASS","a",["PASS","PASS"],0,[["budget","20s"]]]]}