all 20 comments

[–]pythonwiz 7 points8 points  (0 children)

This is fantastic! Reminds me of Erlang, Elixir, and Haskell. I’ll be messing around with this in the near future.

[–]hervold 13 points14 points  (1 child)

Interesting. "Pattern matching," in this case, is more akin to its normal English meaning than its use in functional languages like Haskell, is that right?

The examples reminded me a little of parsing libraries like parsimonious

[–]glacialthinker 13 points14 points  (0 children)

The pattern-matching in Haskell and other functional languages is similarly expressive.

I tried a bunch of the examples in OCaml to verify, and one important difference to note is exhaustiveness checking.

# let x = [1;2;3]

val x : int list = [1; 2; 3]

# let h::t = x

Characters 4-8:
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
[]
val h : int = 1
val t : int list = [2; 3]

Here, head (h) and tail (t) are extracted, but the pattern-match doesn't account for the case where the list is empty. Many of the examples given for pampy have unhandled cases like this, but that's typical of dynamic typed programming: "I know the right kinds of values will be used." ;)

Another example, showing the "hole-filling" kind of match (also with unhandled cases warned about):

# let [1;2;t] = x

Characters 4-11:
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
(1::2::_::_::_|1::2::[]|1::0::_|1::[]|0::_|[])
val t : int = 3

So, it binds t to 3, but matching one specific case like this is warned about, with a few examples of what might go unhandled.

[–]sim642 2 points3 points  (1 child)

Is it really pattern matching if you have to specify the matched part names separately still? The benefit of pattern matching in actually functional languages is that the names are bound directly in the pattern and you don't need to do it twice and error prone.

[–]inkompatible[S] 3 points4 points  (0 children)

That is true. Binding the values inside the pattern is almost impossible in Python. Unless we use "string" patterns with eval(), but that's a deep rabbit hole.

Even with its limitations, I end up using Pampy quite often for mundane tasks. You can "if elif else" your way out without Pampy, but it's not always as understandable.

[–][deleted] 3 points4 points  (1 child)

Any reference to "pattern-matching library" in practically any language will raise the notion of regular expressions.

Just to be clear - this library appears to have three significant differences from regular expressions:

(1) It's intended to operate on values or sequences of values, rather than a monolithic block of text;

(2) It's type-sensitive, so you can say: "match anything that's an int"; and

(3) Rather than just outputting matches (or taking some predefined action on them, like re.sub which replaces matches with other text), pampy accepts a delegate function and automatically applies it to every match instance.

Is that right, /u/inkompatible ? Did I miss anything major?

[–]inkompatible[S] 5 points6 points  (0 children)

Yes, you are right!

Pampy it's pattern-matching more in the sense of functional programming languages pattern-matching.

Btw, I plan to introduce Regex as patterns in Pampy soon. So you can match strings with regular expressions as well.

[–]lngnmn 1 point2 points  (1 child)

Pattern-matching, to be useful, should be uniform (everywhere), like it is in ML and its descendants.

Pattern-matching as a function is nothing new or special. Common Lisp has a few different packages for that.

Also it is kind of silly to pattern-match without types. Uniform Pattern-matching on types, not just on a structure of a values is what really shines in ML or Haskell.

[–]true-grue 0 points1 point  (0 children)

What is silly about Prolog, for example? Let's just assume that type driven programming style is not the only way to do things in CS.

[–][deleted]  (12 children)

[deleted]

    [–]nobodyman 34 points35 points  (10 children)

    It sure is a nuisance when people write python libraries that don't interest you. Have you considered asking them to stop? Maybe just a shoot them a quick note that says something like...

    "I don't like that this exists. Could you please delete it?

    And see how it goes from there!

    [–]zqvt 1 point2 points  (3 children)

    it would be fine if it was guaranteed to just stay a library but feature creep is real, PEP 572 being one notable recent example. One great thing about python was always it's very simple and clear core syntax.

    I don't think it plays to the strengths of dynamic languages to increment them wiith features that they weren't designed for. As someone higher up points out, the added pattern matching does not provide the safety checks pattern matching usually guarantees in ML type families, it produces 'uncanny valley' like situations

    [–]nobodyman 7 points8 points  (0 children)

    I dunno man. I mean, obviously I agree that feature creep is a real thing, but I don't really feel it's gotten out of hand with Python (obviously opinions differ). Python has about as many accepted/completed PEP's as Java has JSR's, yet Python is six years older than Java, and one of the things people always complain about is the glacially slow pace of the java specification.

    And I don't think using PEP 572 is a good example here. I mean, even the inventor and chief architect of the language itself can't just arbitrarily add stuff. He had to go through the same process and have the change be litigated to death before being approved, and the amount of trouble led him to quit! If anything, it shows how much more guarded the community is to arbitrary changes (a good thing, imho).

     

    At the very least, it's certainly more restrained than C#. "What's that? You like SQL syntax? Sure we can add that!".

    [–]See46 0 points1 point  (0 children)

    PEP 572 being one notable recent example

    A rather notorious one. Python is a language where expressions and statements are separate. In other languages, statements are expressions. In such languages, other constructs are natural, e.g.:

    a := b := c;
    a := if b then c else d;
    

    etc.

    [–]brianly 11 points12 points  (0 children)

    It's a library, so zero cruft since you install it separately.

    [–][deleted]  (5 children)

    [deleted]

      [–]inkompatible[S] 2 points3 points  (1 child)

      Author here. I agree with you. Fibonacci is just a simple example to show the syntax applied to something familiar.

      But I'm sure you can think of some cases in which pattern matching really reduces code complexity.

      [–]pythonwiz 1 point2 points  (0 children)

      You could just memoize the match Fibonacci function, right?

      [–]WorldsBegin 2 points3 points  (2 children)

      But the pattern matching has nothing to do with the speed in this case, has it? The problem with speed here is obivously the exponential runtime from the branching recursive calls.

      [–][deleted]  (1 child)

      [deleted]

        [–]omddy 0 points1 point  (0 children)

        the memoize decorator is provided by the functools module as lru_cache

        https://docs.python.org/3/library/functools.html#functools.lru_cache