AdaCore / ada-spark-rfcs

Platform to submit RFCs for the Ada & SPARK languages
63 stars 28 forks source link

[RFC] Structural pattern matching #50

Closed raph-amiard closed 4 years ago

raph-amiard commented 4 years ago

Link to the text: https://github.com/AdaCore/ada-spark-rfcs/blob/topic/pattern_matching/considered/rfc-pattern-matching.rst

sttaft commented 4 years ago

Note that "as" is not a reserved word in Ada, and introducing such a short word as a reserved word this late in the evolution of the language could be very painful, especially in a language that is not case sensitive, so that a short acronym-based name like "AS" could already be heavily used. You might want to consider "is" or "use". E.g.:

<> is V:T   or  <> use V:T

-Tuck

On Wed, Jul 15, 2020 at 11:24 AM Raphaël AMIARD notifications@github.com wrote:


You can view, comment on, or merge this pull request online at:

https://github.com/AdaCore/ada-spark-rfcs/pull/50 Commit Summary

  • RFC pattern matching, first draft
  • Fix pattern matching proposal extension
  • Reword summary
  • Add motivation section
  • Precision about regular case statements in the summary
  • Add note about matching several values
  • Small rewordings & reorgs
  • Code formatting
  • Add sections, some editing
  • Mark all TBD sections
  • Minor rewording
  • add two sentences about semantics of matching on composite
  • add unresolved question about semantics of bindings
  • Some rewrite, fill out missing sections
  • Last edits

File Changes

Patch Links:

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/AdaCore/ada-spark-rfcs/pull/50, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANZ4FIJ3UUP2NWJWLDILR3R3XC2BANCNFSM4O2UQYAA .

yannickmoy commented 4 years ago

I really like the current proposal!!! In particular the rules regarding strict inclusion and ordering are both clear and powerful IMO, and will catch mistakes that are not detected until later in other languages.

Note that the Note in ReST syntax is not well displayed, maybe you can find another directive better supported by GitHub, or switch to markdown syntax.

sttaft commented 4 years ago

Any comment on the use of "as" as a reserved word? "is" and "use" seem like possible alternatives. -Tuck

On Thu, Jul 23, 2020 at 9:14 AM yannickmoy notifications@github.com wrote:

I really like the current proposal!!! In particular the rules regarding strict inclusion and ordering are both clear and powerful IMO, and will catch mistakes that are not detected until later in other languages.

Note that the Note in ReST syntax is not well displayed, maybe you can find another directive better supported by GitHub, or switch to markdown syntax.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/AdaCore/ada-spark-rfcs/pull/50#issuecomment-662999462, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANZ4FMUDAG2SZ6XR3OL6U3R5AZSXANCNFSM4O2UQYAA .

kanigsson commented 4 years ago

IIRC, Ada 2012 uses "some" without making it a reserved word. Couldn't the same trick be used here for "as"?

sttaft commented 4 years ago

Actually, "some" is a reserved word in Ada 2012. We have periodically considered having "unreserved keywords" in Ada, starting way back in Ada 95, and every time, they have been shot down at the ISO WG9 level. So I would highly recommend we propose using an existing reserved word, or pick one that is long and unlikely to be used widely.

-Tuck

On Thu, Jul 23, 2020 at 6:33 PM Johannes Kanig notifications@github.com wrote:

IIRC, Ada 2012 uses "some" without making it a reserved word. Couldn't the same trick be used here for "as"?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/AdaCore/ada-spark-rfcs/pull/50#issuecomment-663264484, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANZ4FM3YJNVMLIYAG5WWZTR5C3CJANCNFSM4O2UQYAA .

raph-amiard commented 4 years ago

@sttaft Thanks for your input. My position on the as keyword:

Lucretia commented 4 years ago
sttaft commented 4 years ago

Thanks for the response! Unreserved keywords have pros and cons. I have periodically pushed hard for them. Ultimately I gave up in the context of the Ada standard, but as you say, this is just a prototype (though prototypes do have a tendency to morph into difficult-to-change constructs ;-).

As an historical note, I suspect some of the fear of unreserved keywords comes from the PL/I experience, where monstrosities like "if if then then ..." were legal in the full version of the language, which had unreserved keywords. At some point, they defined a subset of PL/I which, among other simplifications, eliminated unreserved keywords in favor of reserved keywords. From the Wikipedia entry on PL/I: "Some argue that PL/I is unusually hard to parse. The PL/I keywords are not reserved so programmers can use them as variable or procedure names in programs. Because the original PL/I(F) compiler attempts auto-correction when it encounters a keyword used in an incorrect context, it often assumes it is a variable name. This leads to "cascading diagnostics", a problem solved by later compilers."

-Tuck

On Fri, Jul 24, 2020 at 3:52 AM Raphaël AMIARD notifications@github.com wrote:

@sttaft https://github.com/sttaft Thanks for your input. My position on the as keyword:

  • This RFC is for a prototype, we can definitely decide to change the syntax later, in the way you propose or another.
  • Our intent is to try and prototype it with as being a non reserved word. We'll see how it goes but we can definitely change it at a later stage if the experiment doesn't fare well.
  • If this (hopefully) comes to language level standardisation it will be ample time to discuss again, but I think the concerns you cite are not pressing concerns at this stage, where we should focus on making the most usable feature with the best ergonomics. This won't be hard to change if we need to at a later stage.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/AdaCore/ada-spark-rfcs/pull/50#issuecomment-663394799, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANZ4FLCH2U5QXDNDDTQ6DDR5E4VLANCNFSM4O2UQYAA .

Lucretia commented 4 years ago

Thanks for the response! Unreserved keywords have pros and cons. I have periodically pushed hard for them. Ultimately I

NP.

gave up in the context of the Ada standard, but as you say, this is just a prototype (though prototypes do have a tendency to morph into difficult-to-change constructs ;-). As an historical note, I suspect some of the fear of unreserved keywords comes from the PL/I experience, where monstrosities like "if if then then ..." were legal in the full version of the language, which had

I remember PL/I from computer science, I still have no idea how they parsed that monstrosity.

raph-amiard commented 4 years ago
  • I'm surprised you didn't write a tuple RFC first, then reference that in this one. This would then help adding currying as some people are wanting that. For first class functions, I would say to extend the expression functions for that.

We debated that indeed. I'm not sure how you link that to currying/first class functions in the context of Ada, but we agree that it would be a great combination with pattern matching.

  • Typos, the sentence "We also believe that is brings necessary expressivity" should be "We also believe that it brings necessary expressiveness"

Thanks! We released it for public consumption before we had the chance to get much proofreading by native speakers. We'll do a few passes of corrections in the coming weeks.

  • At library level, there should be a set of option types in a package hierarchy, we don't want to keep redefining this stuff, i.e. Ada.Optionals.

Completely agreed on that. We're taking our time to see how this meshes with other potential improvements, for example to generics.

  • I would get rid of the as X : <type> thing and just stick to the => <X> configuration instead, it's shorter and doesn't require the addition of "as" as a keyword.

Do you propose you cannot write <> as X : Integer but always <X> : Integer ? We considered that but you lose the ability to do 1 .. 100 as X : Integer, or even (<X>, <Y>) as P : Point (for example), which we think is useful.

  • Will there be any short form pattern matchin, i.e. if match...else...?

We're considering it! We wanted to do the smallest possible RFC to start with, but I'll add it in "future enhancements" around the irrefutable patterns section.

Lucretia commented 4 years ago
  • I'm surprised you didn't write a tuple RFC first, then reference that in this one. This would then help adding currying as some people are wanting that. For first class functions, I would say to extend the expression functions for that.

We debated that indeed. I'm not sure how you link that to currying/first class functions in the context of Ada, but we agree that it would be a great combination with pattern matching.

Because in FP languages, tuples are used to pass multiple parameters, as functions there only accept 1 parameter. But I don't know how this all works with Ada.

raph-amiard commented 4 years ago

@Lucretia

Because in FP languages, tuples are used to pass multiple parameters, as functions there only accept 1 parameter. But I don't know how this all works with Ada.

Ok I had a feeling you might be refering to that. Note that currying is distinct from this tuple business. In OCaml, for example, you'll define a curried function as such:

let add x y = x + y;;

and use tuples to defined uncurried functions:

let add (x, y) = x + y;;

So tuples have nothing to do with currying directly, they're just the way to defined n-ary uncurried functions in functional languages.

Anyway:

  1. We're not looking to add currying to Ada, at least not directly (it might still be possible if we manage to add lambdas/closures someday)
  2. Tuples are very interesting in their own right. If you are interested in having them, you should at least open an issue on this repo! :slightly_smiling_face:
reznikmm commented 4 years ago

May be we can add interface types to this proposal if allow getter function in place of a component. Let's define a getter function a one argument function for interface (and private?) type. Then the example with Shape looks like:

type Shape is interface;
function X (Self: Shape) return Integer is abstract;
function Y (Self: Shape) return Integer is abstract;

type Line is interface and Shape;
function X2 (Self: Line) return Integer is abstract;
function Y2 (Self: Line) return Integer is abstract;

type Circle is interface and Shape;
function Radius (Self: Circle) return Integer is abstract;

S : Shape'Class := ...;

case S is
   when Circle'Class'(Radius => 0) => Put_Line ("point");
   when Circle'Class               => Put_Line ("circle");
   when Line'Class                 => Put_Line ("line");
   when <>                         => Put_Line ("other shape");
end case;
Lucretia commented 4 years ago

FYI, for those that don't know, here's the other current (deferred) AI for pattern matching.

sttaft commented 4 years ago

This seems like a nice way to bring interface/private types into the feature. Perhaps you could go further and allow any primitive that can use "." notation, even if it has more than one parameter, so:

when T'Class (Prim(3) => ) => ...

might be allowed, if Prim is declared:

function Prim (A : T; B : Integer) return Integer;

-Tuck

On Mon, Jul 27, 2020 at 3:41 AM Maxim Reznik notifications@github.com wrote:

May be we can add interface types to this proposal if allow getter function in place of a component. Let's define a getter function a one argument function for interface (and private?) type. Then the example with Shape looks like:

type Shape is interface;function X (Self: Shape) return Integer is abstract;function Y (Self: Shape) return Integer is abstract; type Line is interface and Shape;function X2 (Self: Line) return Integer is abstract;function Y2 (Self: Line) return Integer is abstract; type Circle is interface and Shape;function Radius (Self: Circle) return Integer is abstract;

S : Shape'Class := ...; case S is when Circle'Class'(Radius => 0) => Put_Line ("point"); when Circle'Class => Put_Line ("circle"); when Line'Class => Put_Line ("line"); when <> => Put_Line ("other shape");end case;

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/AdaCore/ada-spark-rfcs/pull/50#issuecomment-664176603, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANZ4FMRVOX6PJYVQIBUL7TR5UVTPANCNFSM4O2UQYAA .

clairedross commented 4 years ago

I like the idea Maxim. Let's keep it in mind for when we extend this feauture to private types.

yakobowski commented 4 years ago

Regarding arrays, I think it would be useful to clarify whether it is possible to use constant expressions that involve 'First and 'Last as indices. This is something that seems desirable in practice. For arrays where the type is constrained, we should always be able to decide whether such patterns overlap, in which case we could reject them.

raph-amiard commented 4 years ago

Thanks for the feedback @yakobowski ! Sounds reasonable indeed, we'll do a pass!

Blady-Com commented 4 years ago

Could this proposal cover the following examples?

   type Representation is (Cartesian, Polar);
...
   function Add (Z1, Z2 : Complexe) return Complexe is
      type Couple is array (1..2) of Representation;
   begin
      case Couple'(Z1.R, Z2.R) is
          when (Cartesian, Cartesian) => return (Cartesian, Z1.X + Z2.X, Z1.Y + Z2.Y);
          when (Cartesian, Polar) => return (Cartesian, Z1.X + Real (Z2), Z1.Y + Imag (Z2));
          when (Polar, Cartesian) => return (Cartesian, Real (Z1) + Z2.X, Imag (Z1) + Z2.Y);
          when (Polar, Polar) => return (Cartesian, Real (Z1) + Real (Z2), Imag (Z1) + Imag (Z2));
      end case;
   end Add;

or:

   type Card is record
      S : Suit;
      V : Value;
   end record;
   procedure Play (C : Card) is
   begin
      case C is
         when (Heart, King) => Process_Le_Barbu;
         <...>
   end Play;

and more you may have extended ranges:

         when (Club, Seven) .. (Club, As) => Process_Clubs;

or even:

type TS4 is String(1..4);
procedure (V : TS4) is
  begin
     case V is
        when "PAPA" | "MUM " => Process_Parent;
        when "TOTO" .. "TUTU" => Process_Dummy;
...

See more details in discussion from Ada-Comment.

raph-amiard commented 4 years ago

Hello @Blady-Com, and sorry for the late reply!

      case Couple'(Z1.R, Z2.R) is
          when (Cartesian, Cartesian) => return (Cartesian, Z1.X + Z2.X, Z1.Y + Z2.Y);
          when (Cartesian, Polar) => return (Cartesian, Z1.X + Real (Z2), Z1.Y + Imag (Z2));

This is already covered by the proposal, as is the next example:

         when (Heart, King) => Process_Le_Barbu;
         when (Club, Seven) .. (Club, As) => Process_Clubs;

        when "TOTO" .. "TUTU" => Process_Dummy;

As someone (I think) said in the Ada-Comment thread, the "natural" order of enumeration might not always make sense. Also whether the range is a linear aggregation of the sub-ranges, or a product. All in all it seems too complex and not worth the trouble.