11

This case revealed I am missing something in pattern matching and evaluation in case of match, or I can't grasp it due to the busy week I have. Either way, I find this problem interesting for wider audience:

asso = <|
    "key" -> <|"a" -> "1", "b" -> Compress@"test"|>
|>

We want to replace the inner association with uncompressed value from "b" key. The expected result is:

<|"key" -> "test"|>

Association is HoldAllComplete so we will have to use Trott-Strzebonski technique or RuleCondition.

The problem is it works but throws a message in between:

asso /. KeyValuePattern[ {"a" -> "1", "b" -> val_String}] :> With[
    {eval = Uncompress[val]}
  , eval /; True
]
 (*or RuleCondition@Uncompress[val]*)
Uncompress::argt: Uncompress called with 0 arguments; 1 or 2 arguments are expected.

<|"key" -> "test"|>

The question is, what am I missing? Recent chat discussion confirms that is not obvious.

I have stuff to do so I'm just using the workaround:

asso /. sub : KeyValuePattern[{"a" -> "1", "b" -> _String}
] :>   RuleCondition @ Uncompress @ sub @ "b"

but I don't get the difference that makes it work.

Kuba
  • 136,707
  • 13
  • 279
  • 740
  • 1
    The With code does not give the uncompressed output in Open Cloud. asso /. KeyValuePattern[{"a" -> "1", "b" -> val_String}] :> RuleCondition @ Uncompress @ val does give the uncompressed output but also issues a message. Which part of this is more interesting to you? – Mr.Wizard Jun 30 '17 at 06:43
  • @Mr.Wizard The point is, I want to be comfortable using KeyValuePattern. At the moment I'm not. So any insight is on topic. Also, as pointed out I don't see the reason why my workaround works so I'd say the open cloud kernel is closer to me ;) – Kuba Jun 30 '17 at 06:49
  • The same weird behaviour prevents replacement in this code: <|0 -> 0, 1 -> 2|> /. KeyValuePattern[{a_ -> b_, c_ -> d_}] :> (1 /; {d} =!= {c}) even though the condition is True. – Coolwater Jun 30 '17 at 11:35

1 Answers1

7

The output of this code in Open Cloud seems to reveal what is happening:

<|"a" -> 1, "b" -> 2, "c" -> 3, "d" -> 4|> /.
  KeyValuePattern[{"a"->v1_, "b"->v2_, "c"->v3_, "d"->v4_, "x"->v5_}] :> 
    "foo" /; (Print[{v1},{v2},{v3},{v4},{v5}];True)

{1}{}{}{}{}

{1}{2}{}{}{}

{1}{2}{3}{}{}

{1}{2}{3}{4}{}

<|a->1,b->2,c->3,d->4|>

So we can see that the expression is incrementally tested for partial matches.

Rather like:

{1, 2, 3, 4} /. {x__, ___} :> "foo" /; Print[{x}]

{1}

{1,2}

{1,2,3}

{1,2,3,4}

{1, 2, 3, 4}

This is how we get Uncompress[] in your evaluation as val_String is given a null (vanishing) match in the first attempt.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • But why partial matches are considered match? And if it is only due to the condition, why this does not leak: f[x_Real] := Echo[x^2]; Hold[{Hold[2.], Hold[3.], Hold["string"]}] /. n_Real :> With[{eval = f[n]}, eval /; True] – Kuba Jun 30 '17 at 07:30
  • @Kuba I don't know; this is the first time I have looked at KeyValuePattern. I presume it is part of KeyValuePattern's design, perhaps necessitated by its unusual matching behavior. – Mr.Wizard Jun 30 '17 at 07:32
  • My claim is that either both leak (KeyValuePattern and the example from the comment) or neither. Otherwise this inconsistency is more than a design choice. This or I still miss something. – Kuba Jun 30 '17 at 07:34
  • @Kuba But KeyValuePattern does not work like a simple _ pattern. It specifically allows things like MatchQ[<|a -> 1, b -> 2, c -> 3|>, KeyValuePattern[b -> 2]] (True) which implies ___-like aspects. – Mr.Wizard Jun 30 '17 at 07:37
  • This too but it does not leak: Hold[{{a -> 1, b -> "string", c -> 3}, {a -> 1, b -> 2, c -> 3}}] /. {___, b -> i_Integer, ___} :> With[{eval = Echo[i^2]}, eval /; True] – Kuba Jun 30 '17 at 07:41
  • @Kuba But I think KeyValuePattern is nonstandard, like OptionsPattern and OptionValue, and may display behavior that is not precisely replicable with other pattern expressions. I am not attempting to provide an explanation for the design, merely to illustrate its present behavior. I would have to have much greater familiarity with this functionality before I could guess the designers intent. – Mr.Wizard Jun 30 '17 at 07:45
  • Accepting its special design, I feel it needs more explanation in documentation. Back to the case, what makes my workaround different? – Kuba Jun 30 '17 at 07:47
  • @Kuba Oh, I agree, the documentation is insufficient. Your work-around does not use the pattern val_ so there is no null pattern to disappear within Uncompress and cause the message? – Mr.Wizard Jun 30 '17 at 09:30
  • Yes but if there is matched 'b'-less association then we should get an error like Uncompress[<|"a" -> 1|>["b"]]. Have to go now, will have busy weekend so I may not reply asap in following days. Have a nice weekend and thanks for help. – Kuba Jun 30 '17 at 09:33
  • @Kuba Oh, good point, let me look at that. I hope your busy weekend is good not bad! :-) – Mr.Wizard Jun 30 '17 at 09:34
  • @Kuba It looks like the complete pattern sub only fires after a final match is determined, while the Condition is part of the candidate (attempted match) process. – Mr.Wizard Jun 30 '17 at 09:38
  • This answer clearly addresses the question, but it seems I need more. Like, shouldn't a pattern be predictable? Is KeyValuePattern more than a pattern? How can we classify patterns then? Such case is a clear case that something is not ok. – Kuba Jul 05 '17 at 19:45
  • @Kuba I understand. You'll get no pressure to Accept this from me! Since I don't have KeyValuePattern in the version I use I think someone else will be better qualified to respond to these concerns. My answer is only one step in that process. – Mr.Wizard Jul 06 '17 at 06:18
  • Maybe I should ask another question about the big picture of patterns? This feels like a closed one and I don't want to change it too much. – Kuba Jul 06 '17 at 06:19
  • @Kuba I don't have a recommendation. I think KeyValuePattern is the unusual object so widening this to patterns in general may not help, but I also understand (and appreciate) not changing a question too much. I feel like what we are missing is a canonical explanation of how KeyValuePattern actually operates. I guess that it has or acts like a longer internal pattern form, perhaps a head with Attributes, etc., and this is specially applied to internal Association data. – Mr.Wizard Jul 06 '17 at 06:51