Clojure JIRAhttp://dev.clojure.org/jira
This file is an XML representation of an issueen-us4.464925-07-2011[CLJ-1249] Warning when a record field with the same name as a function exists for ithttp://dev.clojure.org/jira/browse/CLJ-1249
Clojure<p>Hi,</p>
<p>I had the following problem which took me much longer than it should have. I accidentally had a record's field with the same name as one of the functions from a protocol. When I tried to call the function from within the record I got totally weird behavior and didn't find it, until I removed every piece of code when I found the name conflict. </p>
<p>I wish clojure would warn me in such a case. (Disregarding any naming conventions that could have saved me.)</p>
<p>Following a small example to reproduce the problem:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">(defprotocol HasPets
(dogs [<span class="code-keyword">this</span>])
(cats [<span class="code-keyword">this</span>])
(octopus [<span class="code-keyword">this</span>])
(cute-ones [<span class="code-keyword">this</span>]))
; Here the field <span class="code-quote">"dog"</span> is added with the same name as the protocol
(defrecord Petshop [dogs]
HasPets
(dogs [<span class="code-keyword">this</span>]
[:pluto :bethoven])
(cats [<span class="code-keyword">this</span>]
[:tom])
(octopus [<span class="code-keyword">this</span>]
[:henry])
(cute-ones [<span class="code-keyword">this</span>]
; Here it was intended to call the function <span class="code-quote">"dogs"</span>, instead the
; field is used.
(concat (dogs <span class="code-keyword">this</span>) (cats <span class="code-keyword">this</span>))))
(def dogs-in-stock nil)
(let [petshop (-&gt;Petshop dogs-in-stock)]
(println <span class="code-quote">"Dogs <span class="code-keyword">for</span> sale:"</span> (dogs petshop))
(println <span class="code-quote">"All cute animals: "</span> (cute-ones petshop)))</pre>
</div></div>
<p>Results in:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">clojure core.clj
Dogs <span class="code-keyword">for</span> sale: [:pluto :bethoven]
Exception in thread <span class="code-quote">"main"</span> java.lang.NullPointerException
at user.Petshop.cute_ones(core.clj:20)
at user$eval84.invoke(core.clj:26)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6514)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:6955)
at clojure.lang.<span class="code-object">Compiler</span>.loadFile(<span class="code-object">Compiler</span>.java:6915)
at clojure.main$load_script.invoke(main.clj:283)
at clojure.main$script_opt.invoke(main.clj:343)
at clojure.main$main.doInvoke(main.clj:427)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.lang.Var.invoke(Var.java:415)
at clojure.lang.AFn.applyToHelper(AFn.java:161)
at clojure.lang.Var.applyTo(Var.java:532)
at clojure.main.main(main.java:37)</pre>
</div></div>
<p>Expected warning:</p>
<p><tt>Warning: the protocol function "dog" from "HasPets" conflicts with the equally named record field of "Petshop"</tt></p>
<p>Without <tt>dog</tt> as a record's field:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">clojure core.clj
Dogs <span class="code-keyword">for</span> sale: [:pluto :bethoven]
All cute animals: (:pluto :bethoven :tom)</pre>
</div></div>
<p>Thanks for your help.</p>
<p>Ben.</p>Linux, clojure jar or leiningen replCLJ-1249Warning when a record field with the same name as a function exists for itEnhancementMinorClosedDeclinedUnassignedBenjamin PetererrormsgsMon, 26 Aug 2013 13:04:56 -0500Tue, 30 Dec 2014 15:11:17 -0600Fri, 31 Jan 2014 14:41:43 -0600Release 1.4Release 1.502<p>Thanks for the report. </p>
<p>It seems like there is a scoping issue here where <tt>dogs</tt> is bound to the field and there is also a context for it being referred to as a function. It's possible that this should really be treated as a compiler bug and not a warning message problem - that requires some more detective work. Since there is only one function <tt>dogs</tt> (the field name is a function only in keyword form - :dogs), it seems unambiguous what should be done here.</p>
<p>In the meanwhile, presumably a workaround would be to use extend-protocol, etc to attach the protocol to the record outside the record definition.</p><p>Alex I can't see a lack of ambiguity here. I read your comment as saying that because <tt>dogs</tt> is in the call position we can deduce that it's meant to refer to the function rather than the field. But we can't assume that a field is not an <tt>IFn</tt> or intended to be used as one, so I can't make sense of that.</p>
<p>Another workaround should be using a fully qualified reference to the protocol function.</p>
<p>My expectation as a user of defrecord and deftype has always been that ambiguous references always refer to the fields, as if there were a <tt>let</tt> around each function body. So I've done this kind of thing intentionally I think. I don't know what that means about whether or not it should be a warning.</p>
<p>Should warnings for this kind of thing be accompanied by a way to suppress individual instances of the warning? E.g., a <tt>&#94;:no-warn</tt> metadata somewhere?</p><p>It is correct and legal code to have a record or type field shadow a var name, and as Gary mentions, the var is still reachable via a qualified name.</p>
<p>This might be a good warning for a linter like <a href="https://github.com/jonase/eastwood">https://github.com/jonase/eastwood</a>, if it is not present already.</p><p>The Eastwood linter currently has no warning for this situation, but I have created an issue on its Github page to record the enhancement idea: <a href="https://github.com/jonase/eastwood/issues/55">https://github.com/jonase/eastwood/issues/55</a></p><p>Okay, thanks guys.</p><p>I started looking at implementing this in Eastwood today, only to discover that the :local-shadows-var linter implemented in an earlier release on Nov 4 2014 already catches this case and warns about it, among other things. The latest release also works fine for catching this, too.</p><p>Thanks!</p>
<p>Tried it now and got a warning - didn't use eastwood before and tbh would expect it to be a part of the default clojure runtime.</p><p>Output for the example:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">src/petshop/core.clj:21:13: local-shadows-<span class="code-keyword">var</span>: local: dogs invoked as function shadows <span class="code-keyword">var</span>: #'petshop.core/dogs</pre>
</div></div>Global Rank