openlang

(x, x) does not mean both halves are equal

Learners keep reading OCaml's (x, x) as "both halves are equal". It isn't that, and it isn't even legal.

A newcomer tends to reach for one of two stories. In one, the first x names a component and the second checks against it. In the other, every variable in a pattern introduces a fresh binding, so writing x twice is just an error. OCaml requires pattern variables to be linear, so the second story is the true one and the compiler rejects the pattern.

For a tiny exercise I swap it for (left, right) when left = right with a fallback case under it. Learners guess which case (3, 3) lands in. Then change only the second component and ask again with (3, 4).

What I want to land is the gap between naming the parts and testing a relationship between them. I keep going back and forth on whether to state the binding rule up front or let the wrong story fail first.

后续讨论 5

顺着彼此的依据读下去
  1. 固定链接

    One step earlier, then. Let (3, 4) match a plain (left, right) first, with no guard at all, so learners watch it bind. Add the equality guard after that.

  2. 固定链接

    Yes, and I'd split that into two predictions. Ask what the names hold before anyone asks which branch wins. For (3, 4) against the unguarded pair pattern that's left = 3 and right = 4. That alone is a concrete counterexample to the idea that a pair only matches when its halves are equal.

    Then bring in the guard and ask again. Both components still get bound, the equality test fails, and selection moves on to the fallback. Keeping the two guesses apart shows where a learner's model breaks. Someone can be completely right about how names get their values and still believe a failed guard means the pair's shape didn't match.

  3. 固定链接

    Regex is probably where the equality reading comes from. In Perl, /(\w)\1/ really does mean the same character twice, because \1 is a backreference that has to match whatever group one captured. Anyone with a few of those under their belt sees (x, x) and assumes the same rule. Worth naming out loud. A pattern that binds and a pattern that compares can look identical, and the regex habit is a sticky one.

  4. 固定链接

    Ran it in the suggested order with a few learners. Plain (left, right) first, then the guard. The first prediction was easy, everyone had left = 3 and right = 4 for (3, 4). The second split them where it was supposed to. Two people said (3, 4) falls through because the pattern "doesn't match", when both names had been bound and only the guard failed.

    I didn't try explaining it, since a sentence from me is easy to nod along to. I asked both of them to type the version they believed in.

    let same = function
      | (x, x) -> true
      | _ -> false

    The compiler refused it, saying x is bound several times in this matching. They read it twice and said "oh, so the second x is a new name", and both got the guarded (3, 4) right on the next try. Next round I'm putting that error on the screen before the guard.

  5. 固定链接

    A trap for the round after that one. Once learners hear that the guard is where a comparison goes, a few will move the comparison into the arm body instead, | (left, right) -> if left = right then true else false, with the old fallback still sitting underneath. The two placements differ in when they run. A failed guard sends selection on to the next case. An if in the body runs after the arm has already been chosen, so the case underneath can never be reached.

    The compiler does say so, with warning 11 that the match case is unused, but it is a warning and easy to scroll past. For (3, 3) and (3, 4) both versions return the same values, so the predictions won't expose it. Only a fallback that does something different from the else branch will.

同一个问题的其他角度

  1. Notation first, machine later

    APL was a notation before it was a program. Iverson used it on paper to describe algorithms, and the 1962 book A Programming…

    回复 1最近活动 apl
  2. Where Pharo code actually lives

    Poking at Pharo after a long time with Ruby, and I'm stuck on something embarrassingly basic for someone used to files. I mad…

    回复 1最近活动 smalltalk
  3. A useful review comment on a retain cycle names the owner

    “Just use weak everywhere” is a review comment the author can't really answer. The usual case is a child object holding a del…

    回复 1最近活动 swift