Hacker News new | ask | show | jobs
by chriswarbo 1961 days ago
It occurs to me that there's a nice way to understand this from what's happened in Scala.

Scala has always had built-in syntax for pattern-matching, like:

    foo match {
      case bar => ...
      case baz => ...
    }
However, Scala also has a thing called `PartialFunction[InputType, OutputType]`, which is a function defined 'case by case' (it's "partial" because we're allowed to leave out some cases). This is essentially a re-usable set of cases, which we can apply to various values just like calling a function.

For example we can write:

    val f: PartialFunction[A, B] = {
      case bar => ...
      case baz => ...
    }

    f(foo)
Scala also allows us to attach extra methods to certain types of value, via 'implicit classes' (which were added late on in Scala's history, although similar patterns were available before). As of Scala 2.13, the standard library attaches a method called `pipe` to values of every type. The `pipe` method simply takes a function and applies it to this/self. For example:

    val f: PartialFunction[A, B] = {
      case bar => ...
      case baz => ...
    }

    foo.pipe(f)
However, now that we have these two things (`PartialFunction` and `pipe`), it turns out we don't need explicit syntax for `match` at all! We can always turn:

    foo match {
      case bar => ...
      case baz => ...
    }
Into:

    foo.pipe({
      case bar => ...
      case baz => ...
    })
Hence Scala, in a round-about way, has shown us that pattern-matching is essentially a function call.

When it comes to Python, it doesn't even need to be a discussion about block scope; it's equally valid to think of this as function scope (like Python already supports), where `case` acts like `lambda`, except we can define a single function as a combination of multiple `case`s (like in the Scala above).

1 comments

As said many times already, then you have the opposite problem - how to get value from "inner" to "outer" scope. If we talk about function scope, then it requires "nonlocal" declaration in the inner scope. From Python, too many declaration like that are syntactic litter. It has a scoping discipline which allows to avoid them in most cases, and that works great in 90% of cases (popularity of Python and amount of code written in it is there proof).

Yes, there're still remaining 10%, and pattern matching kinda drew attention to those 10%. I'm interested to address those, and invite other interested parties to discuss/work together on that. The meeting place is python-ideas mailing list.

> If we talk about function scope

Note that I'm not simply saying 'match should have function scope', I'm saying that 'case' is literally a function definition. Hence functions defined using the 'case' keywork should work the same as functions defined using other keywords ('def', 'lambda' or 'class').

> you have the opposite problem - how to get value from "inner" to "outer" scope

The same way as if we defined the function using 'lambda' or 'def' or 'class'

> it requires "nonlocal" declaration in the inner scope

That's not a general solution, since it doesn't work in 'lambda'; although this exposes the existing problem that there is already a difference between functions defined using 'def'/'class' and functions defined using 'lambda'. Adding yet another way to define functions ('case') which defines functions that act in yet another different way just makes that worse.

> I'm saying that 'case' is literally a function definition

And I don't agree with saying it like that. I would agree with "a 'case' could be seen as a function definition". In other words, that's just one possible way to look at it, among others.

Note that from PoV of the functional programming, everything is a function. And block scope is actually recursively lexical lambda.

And OTOH function inlining is a baseline program transformation. Currently in Python, whether a syntactic element (not explicitly a function) gets implemented as a function is an implementation detail. For example, comprehension happen to be implemented as functions. But just as well they could be inlined.

Note that function calls are generally expensive, and even more so in Python. Thus, any optimizing Python implementation would inline whenever it makes sense (called once is obviously such a case). (CPython hardly can be called an optimizing impl, though since 3.8, there's noticeable work on that).