-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement ADTs, and pattern matching #34
Comments
Almost immediately after I came up with pattern matching, I wanted to use it for primitive values too, not just ADTs. I haven't decided yet whether this is an inane idea, or a very worthy one. NoneNo-one is interested in matching on IntThe problem with division is if you give people division on
I briefly toyed with a
But... meh. What's that 'Course, with the new object system we could instead return something like Then I realized pattern matching could be co-opted to "destructure" a number into its
Here the Note the assumptions that go into the We can also check for divisibility by setting
We can even check for primality by parameterizing both factors in the multiplication. Here's a function that decomposes an integer into its constituent prime factors:
That, I must say, looks very nice. (A silent assumption made in the Str(Added later: Please consider this whole section highly conjectural and not something we're likely to add in its current form. See this later comment for why I no longer think this is a good idea.) We can easily check the beginning, middle, and end of a string for patterns:
This eliminates Perl 6's If we throw (a hypothetical)
The only slight difficulty we're sweeping under the carpet here is how greedy the parameterized substring captures should be. If they're all greedy, then we'd find the last The trimming example would definitely want the first Here's an implementation of
ArraySimilarly, one could do destructuring of arrays, using
|
Here's one possible solution: introduce two different concatenation operators Rewriting the examples using this notation:
|
Seems like we're mixing grammars into something weird. Also, couldn't ADTs be implemented with macros? |
Aye. It was a thought experiment, but I don't like it much now that I see the result. Interestingly, substructural matching on Ints is such a success essentially because of the fundamental theorem of arithmetic, which gives all nonzero integers a unique decomposition into factors. But I think it would be possible to combine the Anyway, the important bit of the
Yes, I sure hope so. We just need the following tech in 007:
"Just". 😸 |
Another interesting bit: F#'s active patterns. |
Active patterns, eh? Looks very nice. I'll have to mull over that for a while. |
... Though I have no idea how to provide exhaustivity checks in some cases :). |
Coming back to the |
I came back to this issue to provide an
One thought occurred to me as I was thinking about the above version: there's a notion of "simple statement" that we're leaning on but haven't made explicit in 007. Expression statements like The same conceptual subdivision would serve us if we introduced |
I coded an Elixir ADT module, and it seems it'll be getting exhaustivity checking soon(tm). The code is mostly cleanly contained, but Elixir doesn't really prevent you from creating dynamically-named modules. |
Not sure how much of a problem this is, but...
Those
In short, something like |
Erlang picks the action-at-a-distance one (add a declaration, and some later bind in scope became a match), Elixir picks the always-bind, using I'd rather we go the Elixir route for clarity's sake. |
Yes, agree. |
Between this and #13 I believe 007 becomes a superset of Prolog. |
I'm going to close this one; not because it's not going to happen, but because it's a bit too ambitious as-is, the ideas in it have drifted since it was opened, and what we're actually going to do is adequately covered by four other issues:
|
Here's the proposed syntax, based on the same declaration in an article by Oleg:
Semantically, the
type Exp
declaration would introduce five new types into the current scope, the latter four of which are instantiable classes. (For example,Lam(Var("foo"), Var("foo"))
.) So far, nothing particularly exciting about that. As you can see, ADTs can be recursively defined without much fanfare. If they don't have a base case, you simply won't be able to instantiate one using a finite program.The
|
things stand out a little and make it clear that this is a DSL. They're also a little bit easier on the eyes than;
at the end of the line, and visually hint that there is something very declarative going on here, rather than imperative;
stuff.We also introduce a case statement:
There's both a
case
statement and (as above) acase
expression. Both require an xblock. Notice how the individual cases mirror the syntax of the ADT declaration itself, although here we're binding against values instead of declaring their types. The thing after the colon can be either an expression or a block.In the case of the
App
andLet
cases above,e1
ande2
are introduced as lexical variables whose scope is only the expression or block immediately following the colon. Written as imperative code, theApp
case would come out as something like this:The underscores simply mean "no binding". (I wanted to do asterisks, following Perl 6's lead, but it didn't look nice. Dollar signs don't make sense in 007 because we don't have sigils.) In the case of
Var
we don't care about any of the properties, and we could elide the(_)
completely. But even a paren full of underscores would do shape-checking on the type, which provides a little bit of extra consistency.I guess we could also allow multiple matchers with
|
between them. Hm.In this particular instance that helps us. I'm not sure it's worth the extra complexity with the matching machinery, though. We'd need to throw an error if there was a type mismatch somewhere, or if two matchers didn't introduce exactly the same variables.
If you do
otherwise
as the last case, it will match when nothing else matched. Notice that you can't match on structure with this one, though; it's just a catch-all.The compiler statically detects whether you've "covered all the cases". Given what we've said so far, that looks entirely tractable, even with things such as nested matching and multiple parameters. If you haven't covered all the cases, and don't have an
otherwise
, the compiler fails and describes in poignantly descriptive prose what you missed.I'm sorely tempted to allow individual cases to happen right inside any function
or pointy block, just binding directly against its signature. Both thecase
statement itself andreturn
could then be implicit. Thedepth
sub could then be written as:This kind of "implicit block case matching" would go a long way to compensate for our lack of
multi
things. In fact, I'd say it's a pretty competitive alternative, with a bunch of advantages of its own.It is unclear how much we need visitors à la #26 when we have pattern matching like this. But let's do both and see which one wins. :) Who knows, maybe they'll end up occupying different niches.
The text was updated successfully, but these errors were encountered: