<!-- 
RSS generated by JIRA (4.4#649-r158309) at Sun May 26 04:02:01 CDT 2013

It is possible to restrict the fields that are returned in this document by specifying the 'field' parameter in your request.
For example, to request only the issue key and summary add field=key&field=summary to the URL of your request.
For example:
http://dev.clojure.org/jira/si/jira.issueviews:issue-xml/MATCH-48/MATCH-48.xml?field=key&field=summary
-->
<rss version="0.92" >
<channel>
    <title>Clojure JIRA</title>
    <link>http://dev.clojure.org/jira</link>
    <description>This file is an XML representation of an issue</description>
    <language>en-us</language>    <build-info>
        <version>4.4</version>
        <build-number>649</build-number>
        <build-date>25-07-2011</build-date>
    </build-info>

<item>
            <title>[MATCH-48] Guards cannot be fn expressions</title>
                <link>http://dev.clojure.org/jira/browse/MATCH-48</link>
                <project id="10050" key="MATCH">core.match</project>
                        <description>&lt;p&gt;Anonymous function literals as guards (such as (a :when #(odd? %))) seem to confuse the match compiler.  The attached test shows how.&lt;/p&gt;</description>
                <environment></environment>
            <key id="15107">MATCH-48</key>
            <summary>Guards cannot be fn expressions</summary>
                <type id="1" iconUrl="http://dev.clojure.org/jira/images/icons/bug.gif">Defect</type>
                                <priority id="4" iconUrl="http://dev.clojure.org/jira/images/icons/priority_minor.gif">Minor</priority>
                    <status id="5" iconUrl="http://dev.clojure.org/jira/images/icons/status_resolved.gif">Resolved</status>
                    <resolution id="1">Completed</resolution>
                                <assignee username="dnolen">David Nolen</assignee>
                                <reporter username="chrismgray">Chris Gray</reporter>
                        <labels>
                    </labels>
                <created>Tue, 10 Jan 2012 13:24:39 -0600</created>
                <updated>Sat, 25 Feb 2012 18:49:42 -0600</updated>
                    <resolved>Sat, 25 Feb 2012 18:49:42 -0600</resolved>
                                                                    <due></due>
                    <votes>0</votes>
                        <watches>0</watches>
                        <comments>
                    <comment id="27526" author="dnolen" created="Tue, 10 Jan 2012 22:04:19 -0600"  >&lt;p&gt;I&apos;m on the fence about allowing inline fn expressions and fn literals as guards. The problem is that they can&apos;t be checked for equality and thus tests cannot be shared across guard patterns. I need to think about it some more but I don&apos;t consider this high priority in the near future. Any decision will have to take into consideration the goal of predicate dispatch.&lt;/p&gt;</comment>
                    <comment id="27532" author="chrismgray" created="Wed, 11 Jan 2012 12:41:31 -0600"  >&lt;p&gt;Sorry, I think the fact that the functions in my earlier example were function literals was a bit of a red herring.  The following test also fails.&lt;/p&gt;

&lt;div class=&quot;code panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;codeContent panelContent&quot;&gt;
&lt;pre class=&quot;code-java&quot;&gt;(deftest guard-pattern-match-5
  (is (=
       (let [oddp odd?]
         (match [1 2]
                [a :when odd? b :when odd?] :a1
                [a :when oddp _] :a2
                [_ b :when even?] :a3
                :&lt;span class=&quot;code-keyword&quot;&gt;else&lt;/span&gt; :a4))
       :a2)))&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;</comment>
                    <comment id="27533" author="dnolen" created="Wed, 11 Jan 2012 12:44:32 -0600"  >&lt;p&gt;The earlier examples where not a red herring. This is likely a separate issue.&lt;/p&gt;</comment>
                    <comment id="27534" author="chrismgray" created="Wed, 11 Jan 2012 12:50:37 -0600"  >&lt;p&gt;I really don&apos;t see how, given that there seems to be no code that specializes on the type of function given to a guard.  My guess is that when guard-pattern-match-5 succeeds, guard-pattern-match-4 will succeed as well.  &lt;/p&gt;</comment>
                    <comment id="27535" author="dnolen" created="Wed, 11 Jan 2012 13:00:16 -0600"  >&lt;p&gt;Oh sorry you are right. This is exactly same problem. We can&apos;t know that odd? and oddp are the same. Again this is not something I&apos;m interested in fixing without a lot more consideration.&lt;/p&gt;

&lt;p&gt;Basically functions can&apos;t be tested for equality like types can. This means that the presence of a guard must create a backtrack point. However if we make guards work a little more like types (you have to declare them ahead of time) we lose a little bit of convenience but gain a lot of reasoning power and can share tests and avoid these pitfalls.&lt;/p&gt;

&lt;p&gt;This discussion probably needs a design page. &lt;/p&gt;</comment>
                    <comment id="27539" author="chrismgray" created="Wed, 11 Jan 2012 18:11:21 -0600"  >&lt;p&gt;Two more examples of the same problem, this time not using guards:&lt;/p&gt;

&lt;div class=&quot;code panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;codeContent panelContent&quot;&gt;
&lt;pre class=&quot;code-java&quot;&gt;(deftest unequal-equal-tests
  (is (=
       (match [&lt;span class=&quot;code-quote&quot;&gt;&quot;foo&quot;&lt;/span&gt; &lt;span class=&quot;code-quote&quot;&gt;&quot;bar&quot;&lt;/span&gt;]
              [#&lt;span class=&quot;code-quote&quot;&gt;&quot;.*&quot;&lt;/span&gt; #&lt;span class=&quot;code-quote&quot;&gt;&quot;baz&quot;&lt;/span&gt;] :a1
              [#&lt;span class=&quot;code-quote&quot;&gt;&quot;foo&quot;&lt;/span&gt; _] :a2
              [_ &lt;span class=&quot;code-quote&quot;&gt;&quot;bar&quot;&lt;/span&gt;] :a3
              :&lt;span class=&quot;code-keyword&quot;&gt;else&lt;/span&gt; :a4)
       :a2)))

(deftest unequal-equal-tests-2
  (is (=
       (let [a 1]
        (match [1 2]
               [(:or 1 2) 3] :a1
               [(:or 2 a) 2] :a2
               [1 _] :a3
               :&lt;span class=&quot;code-keyword&quot;&gt;else&lt;/span&gt; :a4))
       :a2)))&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;</comment>
                    <comment id="27541" author="dnolen" created="Wed, 11 Jan 2012 18:58:21 -0600"  >&lt;p&gt;They are not the same problem. These don&apos;t work for very different reasons. The first I probably won&apos;t even address and will leave for the community to deal with - I don&apos;t need robust pattern matching on regexes.&lt;/p&gt;

&lt;p&gt;The second example is a legitimate bug around matching locals which is unrelated to this ticket. Feel free to open a new one for it.&lt;/p&gt;</comment>
                    <comment id="27542" author="chrismgray" created="Wed, 11 Jan 2012 19:15:36 -0600"  >&lt;p&gt;Yes, you&apos;re right, the second is unrelated.&lt;/p&gt;</comment>
                    <comment id="27545" author="chrismgray" created="Thu, 12 Jan 2012 11:20:08 -0600"  >&lt;p&gt;On further reflection, what all these examples show is that Maranget&apos;s algorithm is only correct for literals whose equality you can test at compile time.  Thus, not even locals will work using his algorithm.  Regexes and functions will certainly not work correctly 100% of the time.&lt;/p&gt;

&lt;p&gt;What happens is that when multiple unequal tests in the same column can return a truthy value, you end up with a decision forest rather than a decision tree.  If the first decision tree in the forest has the first and third end-states, while the second tree has the second end-state, if you end up in the third end-state, you must still check the second decision tree before you decide which end-state is actually correct.&lt;/p&gt;

&lt;p&gt;This is a shame, since it means that compiling the matches becomes more complex.  On the other hand, it seems like a great subject for a paper at programming-language conference, so there&apos;s always that. &lt;img class=&quot;emoticon&quot; src=&quot;http://dev.clojure.org/jira/images/icons/emoticons/smile.gif&quot; height=&quot;20&quot; width=&quot;20&quot; align=&quot;absmiddle&quot; alt=&quot;&quot; border=&quot;0&quot;/&gt;&lt;/p&gt;

&lt;p&gt;On a serious note, though, this bug is major, and you should consider removing support for at least guards, locals, and regexes until it is fixed.  The bugs that arise from it in the end-user&apos;s code are really hard to track down &amp;#8211; it&apos;s as if `or` or `and` were broken 10% of the time. &lt;/p&gt;</comment>
                    <comment id="27546" author="dnolen" created="Thu, 12 Jan 2012 11:29:44 -0600"  >&lt;p&gt;There is nothing wrong with Maranget&apos;s algorithm. We just have to be sure that we create a backtrack point - that&apos;s all.&lt;/p&gt;

&lt;p&gt;Functions cannot be fixed because function equality is undecideable. So for guards we &lt;b&gt;might&lt;/b&gt; create a backtrack point. I&apos;ve already updated the README to describe what works at the moment. I have a branch which throws an error if :when is not given a vector of symbols or a symbol. It should probably be improved so that symbols are known top levels (no reassigning fns to locals).&lt;/p&gt;

&lt;p&gt;Regex equality can probably be made to work but I&apos;m not going to do it. (On further thought we can probably make patterns create backtrack points by default, can be overridden for those willing to make their patterns highly optimizeable)&lt;/p&gt;

&lt;p&gt;Locals can be fixed, we&apos;ll definitely create a backtrack point for these.&lt;/p&gt;</comment>
                    <comment id="27547" author="chrismgray" created="Thu, 12 Jan 2012 11:54:14 -0600"  >&lt;p&gt;I&apos;m sorry, but this problem will not be solved by backtracking alone.  At least not with the backtracking mechanism that currently exists. &lt;/p&gt;

&lt;p&gt;With backtracking, you are still treating the problem as though you have a decision tree.  A decision tree requires that all the tests at its nodes are mutually exclusive.&lt;/p&gt;

&lt;p&gt;By assuming that you have a decision tree, once a match is found in the tree, that match is returned.  As my last comment pointed out, that is not sufficient.  You must also check to see if there is a match in an end-state that was declared earlier.  I really don&apos;t see that that&apos;s possible given the current backtracking system.  &lt;/p&gt;</comment>
                    <comment id="27548" author="dnolen" created="Thu, 12 Jan 2012 13:26:19 -0600"  >&lt;p&gt;I don&apos;t follow you. Maranget&apos;s algorithm is not sufficient for pattern matching if we don&apos;t constrain columns to specific types. There are many things already in place to deal with the shortcomings of Maranget&apos;s approach given what we want to accomplish - for example, we actually have a grouping pass. This is the same approach that Racket uses as far as I can tell and they don&apos;t have any problems. Certainly none of the patterns you shown thus that far (besides fns exprs) pose any challenges that I can see.&lt;/p&gt;</comment>
                    <comment id="27549" author="chrismgray" created="Thu, 12 Jan 2012 13:42:07 -0600"  >&lt;p&gt;Sorry, I was editing my comment as you made yours.  I hope it is more clear now.&lt;/p&gt;

&lt;p&gt;I guess I don&apos;t totally understand your code still, so I will try to rectify that before commenting again.  From what I have seen, though, you are trying to build a decision tree.  What I am saying is that isn&apos;t possible at compile time, since you can&apos;t ensure that the nodes of the tree are mutually exclusive.&lt;/p&gt;</comment>
                    <comment id="27550" author="dnolen" created="Thu, 12 Jan 2012 13:51:48 -0600"  >&lt;p&gt;All known constructors are considered mutually exclusive. We group all constructors in a column preserving order as closely as possible. Decisions trees are created for these constructors. If we cannot know at compile time whether something is mutually exclusive (wildcards, locals), we create a backtrack point to handle them.&lt;/p&gt;

&lt;p&gt;Consider that if we only have backtrack points (no trees) all tests could potentially be tried. Our approach is a hybrid one - we don&apos;t rely only on decisions trees and we don&apos;t rely only on backtracking.&lt;/p&gt;

&lt;p&gt;When we get to integrating pattern matching on interfaces, protocols ambiguities of course become possibly. But even this can probably be handled reasonably with something like &quot;prefers&quot;.   &lt;/p&gt;</comment>
                    <comment id="27551" author="chrismgray" created="Thu, 12 Jan 2012 15:07:35 -0600"  >&lt;p&gt;Consider the following pair of decision trees:&lt;/p&gt;

&lt;div class=&quot;preformatted panel&quot; style=&quot;border-width: 1px;&quot;&gt;&lt;div class=&quot;preformattedContent panelContent&quot;&gt;
&lt;pre&gt;  1
 /
a
 \
  3*

  2*
 /
b--4*
 \
  5*
&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Where the numbers are the order in which the terminals appear, and they have stars beside them if they match.  The correct terminal for core.match to return in this case is the second one.  Currently, the code would return the third terminal.  Suppose, however, that backtracking was added so that the tree rooted at b was checked for matches as well.  This is certainly possible, though a lot of information would need to be kept about the match already found (which is what I meant about things not working with the current backtracking system).  Also, you must ensure that the testing stops when you hit the second terminal, for two reasons &amp;#8211; first, not doing so would imply that all terminals are checked, and second, the test to distinguish 4 from 5 could throw an exception.  For similar reasons, the return value of the third terminal can&apos;t be computed &amp;#8211; it could be a very long computation, or it could throw an exception. &lt;/p&gt;</comment>
                    <comment id="27552" author="dnolen" created="Thu, 12 Jan 2012 15:28:29 -0600"  >&lt;p&gt;If the correct terminal is the second one, we will return the second one. No information needs to be kept around. I suggest you take a closer look at the code at this point.&lt;/p&gt;</comment>
                    <comment id="27553" author="chrismgray" created="Thu, 12 Jan 2012 18:58:24 -0600"  >&lt;p&gt;Aah, you&apos;re right.  (I think.)  Might it be more accurate to say that the situation I proposed can&apos;t happen?  That is, no two trees are created where there are lower-numbered terminals in the second tree than in the first tree?&lt;/p&gt;

&lt;p&gt;Is the plan to add backtrack points for everything where equality can&apos;t be determined at compile time? &lt;/p&gt;</comment>
                    <comment id="27554" author="chrismgray" created="Thu, 12 Jan 2012 23:27:20 -0600"  >&lt;p&gt;These patches implement the proper backtracking for tests that are not mutually exclusive.&lt;/p&gt;</comment>
                    <comment id="27558" author="dnolen" created="Fri, 13 Jan 2012 09:15:21 -0600"  >&lt;p&gt;Wow, this is great. I&apos;ve skimmed over the patches and they look pretty good. I will go over them more closely as soon as I can - there are a couple changes we should probably make. Thanks!&lt;/p&gt;</comment>
                    <comment id="27559" author="chrismgray" created="Fri, 13 Jan 2012 11:08:59 -0600"  >&lt;p&gt;No problem.  My apologies for the persistent misunderstanding. &lt;img class=&quot;emoticon&quot; src=&quot;http://dev.clojure.org/jira/images/icons/emoticons/smile.gif&quot; height=&quot;20&quot; width=&quot;20&quot; align=&quot;absmiddle&quot; alt=&quot;&quot; border=&quot;0&quot;/&gt;&lt;/p&gt;</comment>
                    <comment id="27721" author="dnolen" created="Tue, 14 Feb 2012 16:39:42 -0600"  >&lt;p&gt;Sorry for the epic delay. Here are my notes on that patches:&lt;/p&gt;

&lt;p&gt;1. Let&apos;s rename mutually-exclusive-inequality? to comparable?&lt;br/&gt;
2. I want GuardPatterns to comparable, I&apos;m not going to support arbitrary fns - not worth it IMO.&lt;br/&gt;
   As a compromise I&apos;d be willing to rename :when patterns -&amp;gt; PredicatePatterns and GuardPatterns&lt;br/&gt;
   can be allowed via :guard.&lt;br/&gt;
3. No need to declare OrPatterns as comparable - they aren&apos;t real patterns&lt;br/&gt;
4. No need to exclude VectorPatterns - the way that pattern matching works makes this unnecessary&lt;/p&gt;

&lt;p&gt;If you make these changes I promise to apply these in a timely manner &lt;img class=&quot;emoticon&quot; src=&quot;http://dev.clojure.org/jira/images/icons/emoticons/smile.gif&quot; height=&quot;20&quot; width=&quot;20&quot; align=&quot;absmiddle&quot; alt=&quot;&quot; border=&quot;0&quot;/&gt; &lt;/p&gt;</comment>
                    <comment id="27723" author="chrismgray" created="Wed, 15 Feb 2012 12:50:45 -0600"  >&lt;p&gt;Okay, patch uploaded.  There is a fairly significant portion of it that is just cut and paste, which I&apos;m not so happy about, but I don&apos;t think there&apos;s a way to do type inheritance, so I did the easier thing.&lt;/p&gt;</comment>
                    <comment id="27875" author="dnolen" created="Sat, 25 Feb 2012 18:49:42 -0600"  >&lt;p&gt;Fixed, &lt;a href=&quot;https://github.com/clojure/core.match/commit/ac92c6df3f70f56fbe12f9d3f46585e66102c50b&quot;&gt;https://github.com/clojure/core.match/commit/ac92c6df3f70f56fbe12f9d3f46585e66102c50b&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;addressed &lt;a href=&quot;http://dev.clojure.org/jira/browse/MATCH-50&quot; title=&quot;locals pattern matching issue&quot;&gt;&lt;del&gt;MATCH-50&lt;/del&gt;&lt;/a&gt;&lt;/p&gt;</comment>
                </comments>
                    <attachments>
                    <attachment id="10770" name="0001-Added-new-multimethod-mutually-exclusive-inequality.patch" size="2929" author="chrismgray" created="Thu, 12 Jan 2012 23:27:20 -0600" />
                    <attachment id="10764" name="0001-Failing-test.patch" size="1032" author="chrismgray" created="Tue, 10 Jan 2012 13:24:39 -0600" />
                    <attachment id="10769" name="0002-Use-the-new-multimethod-and-add-some-tests.patch" size="4606" author="chrismgray" created="Thu, 12 Jan 2012 23:27:20 -0600" />
                    <attachment id="10910" name="new-patches.diff" size="17722" author="chrismgray" created="Wed, 15 Feb 2012 12:47:35 -0600" />
                </attachments>
            <subtasks>
        </subtasks>
                <customfields>
                                                                                            <customfield id="customfield_10010" key="com.pyxis.greenhopper.jira:gh-global-rank">
                <customfieldname>Global Rank</customfieldname>
                <customfieldvalues>
                    
                </customfieldvalues>
            </customfield>
                                            <customfield id="customfield_10000" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                <customfieldname>Patch</customfieldname>
                <customfieldvalues>
                        <customfieldvalue key="10002">Code and Test</customfieldvalue>

                </customfieldvalues>
            </customfield>
                                                                                        </customfields>
    </item>
</channel>
</rss>