Welcome! Please see the About page for a little more info on how this works.

0 votes
in ClojureScript by
Two namespaces:


(ns my.ns)

(defrecord A [foo bar])

(defn make-a [foo bar]
  (A. foo bar))



(ns other.ns
  (:require [my.ns :refer (A)]))

(println (my.ns/A. 1 2) (my.ns/make-a 1 2))

(defrecord A [baz quux])

(println (my.ns/A. 1 2) (my.ns/make-a 1 2))


Loading {{other.ns}} yields output:


#my.ns.A{:foo 1, :bar 2} #my.ns.A{:foo 1, :bar 2}
#my.ns.A{:baz 1, :quux 2} #my.ns.A{:baz 1, :quux 2}


The definition of {{other.ns.A}} is _replacing_ that of {{my.ns.A}}.

4 Answers

0 votes
by

Comment made by: cemerick

This may be a duplicate of CLJS-1558...?

0 votes
by

Comment made by: mfikes

This ticket shares similarities with CLJS-1558 in that the compiler is essentially allowing you to re-{{def}} a referred Var. Perhaps for both tickets this should cause a compiler error as occurs with Clojure.

0 votes
by
_Comment made by: mfikes_

I struggle a little with this one in terms of defining what _should_ be going on, and at the core of my lack of clarity is on whether {{my.ns/A}} is a Var or simply a type.

In the compiler state, it does show up under {{:defs}}, but it is flagged with {{:type true}}. (In other words, perhaps just because it is under {{:def}} doesn't imply it is a Var.) In Clojure, even though an underlying constructor exists that can be accessed via interop, no actual Var is created. (Evidence backing this assertion is that {{(var A)}} fails in Clojure, and no Var appears if you use {{dir}} at the REPL—you only see the associated synthetically-generated positional and map factory Vars.

Another argument that "it is not a Var" is what {{defrecord}} returns. {{defrecord}} doesn't return a Var (even in Clojure), but instead returns a symbol (which evaluates to itself, much like the symbol {{java.lang.String}}).

If you go with the argument that the underlying constructor is not a Var, then perhaps some things could be cleaned up (fixing {{dir}} in ClojureScript, and perhaps causing {{var}} to balk if applied to types).

Also, if you go with the argument that it is not a Var, then perhaps you'd argue that {{import}} is appropriate, while {{require}} is not. Inspired by http://puredanger.github.io/tech.puredanger.com/2010/06/30/using-records-from-a-different-namespace-in-clojure/, this variation seems to work fine:


(ns my.ns)

(defrecord A [foo bar])



(ns other.ns
  (:require [my.ns])
  (:import (my.ns.A)))

(println (my.ns.A. 1 2) (my.ns/->A 1 2))

(defrecord A [baz quux])

(println (my.ns.A. 1 2) (my.ns/->A 1 2))

(println (A. 3 4) (->A 3 4))


with this result:


cljs.user=> (require 'other.ns)
#my.ns.A{:foo 1, :bar 2} #my.ns.A{:foo 1, :bar 2}
#my.ns.A{:foo 1, :bar 2} #my.ns.A{:foo 1, :bar 2}
#other.ns.A{:baz 3, :quux 4} #other.ns.A{:baz 3, :quux 4}
nil


One thing I don't like is that you have to write {{(:import (my.ns.A))}} as opposed to {{(:import (my.ns A))}, but perhaps that is a minor quirk that could be cleaned up if the "not a Var" approach were taken.
0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJS-2421 (reported by cemerick)
...