One of the things which has been puzzling me is how to pattern match abstract data types. This puzzle reawakened by considering RAList.
An RAList, like a list, supports functions: empty () cons (elt, list) and it supports isempty (list) head (list) when not isempty tail (list) and also uncons = head,tail. Now the secret of list pattern matching is that empty () => 0, () cons (elt,list) => 1, (elt,list) in other words, the constructors Empty = 0 Cons = 1, and the terms constructed are just tuples: constructor, argument so a pattern match says: if run time constructor tag = 0, then empty() was called, argument is () = 1, then cons was called, so argument is (elt, list) and given the argument is at a known offset just after the tag, we can just cast it, and we have got the function's argument out. Clearly, nested patterns are handled by successive application of the above mechanism. So carefully note that the constructor tag is basically the selector for a particular projection function to extract the arguments from the expresion tuple (i.e. its the second projection, which depends on the result of applying the first projection). For a tuple (rather than a variant), the projections are statically known. However, Felix doesn't implement it quite like this. Instead it has two functions: a match comparator, and a match handler. The match handler is user code but includes the match extractor, the bit that, once you know the type, applies the projection chains needed to set the match variables used by the match handler. Now, my critical observation is this: we can rewrite this to if isempty(arg) then () else if not isempty(arg) then var elt,list = uncons (arg) Let's be more precise: suppose for some ABSTRACT data type T, we have constructor FUNCTIONS C0 : A0 -> T C1: A1 -> T ,,, and we have corresponding match checker functions IS0 : T -> bool IS1 : T -> bool ... and we have also corresponding extractors X0 : T -> A0 X1: T -> A1 ... then we can do a "general" decode of the abstract data type like: if IS0 a then H0 (X0 a) elif IS1 a then H1 (X1 a) ... where H0, H1, .. is the user handler code in the match branch, which is a function accepting a tuple of the type of the argument of the constructor C0, C1, etc. So the critical thing is we must list for the abstract data type these NAMED triples: N0: C0, IS0, X0 N1: C1, IS1, X1 The types must be as above: C0: A0 -> T, X0: T -> A0 (precondition IS0 T), however there's more: clearly: a = X0 (C0 a) In other words, X0 has to "undo" the effect of applying C0 to get back the argument to C0. So any old functions, even of the right types, aren't good enough. I have a gut feeling this is a categorical adjunction. Why the names? Obviously so we can now write: match expr with | N0 (?a0) => ... | N1 (?a1) => ... ... The match checker applied is ISi for name i, and the extractor Xi is used if the checker succeeds. Both the checker and extractors are ordinary functions so composition, that is, nested patterns, will work with the existing algorithm. In fact for pattern matching, we do not require the C0 component, however it could be useful as follows: define N0 (a) by C0 (a) In other words, make Ni a synonym for the function Ci so it looks like a other union types: we have a constructor name Ni which can be pattern matched. Note arbitrary abstract types might not admit this representation. Note that if you have an object with accessor functions, one could always provide the extractor (and checker for the precondition), and the name, but no constructor function. Conversely, one could provide a constructor with no checker or extractor. For example we might provide match 1.0 + 2.0 i with | Imag ?y => // imaginary part .. Here there's no constructor because we don't know the real part: the extractor loses it (assuming the checker was universally true, rather than checking for a pure imaginary!) On the other hand this looks cool: match z with | Polar (?angle, ?length) => ... The constructors don't have to be disjoint, nor the destructors. SO: I think I have found a good way to generalise pattern matching. -- john skaller skal...@users.sourceforge.net http://felix-lang.org ------------------------------------------------------------------------------ Want excitement? Manually upgrade your production database. When you want reliability, choose Perforce Perforce version control. Predictably reliable. http://pubads.g.doubleclick.net/gampad/clk?id=157508191&iu=/4140/ostg.clktrk _______________________________________________ Felix-language mailing list Felix-language@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/felix-language