12

Imagine the task is to create some utility lib in clojurescript so it can be used from JS.

For example, let's say I want to produce an equivalent of:

    var Foo = function(a, b, c){
      this.a = a;
      this.b = b;
      this.c = c;    
    }

    Foo.prototype.bar = function(x){
      return this.a + this.b + this.c + x;
    }

    var x = new Foo(1,2,3);

    x.bar(3);           //  >>  9    

One way to achieve it I came with is:

    (deftype Foo [a b c])   

    (set! (.bar (.prototype Foo)) 
      (fn [x] 
        (this-as this
          (+ (.a this) (.b this) (.c this) x))))

    (def x (Foo. 1 2 3))

    (.bar x 3)     ; >> 9

Question: is there more elegant/idiomatic way of the above in clojurescript?

Lambder
  • 2,953
  • 1
  • 26
  • 20

2 Answers2

20

This was solved with JIRA CLJS-83 by adding a magic "Object" protocol to the deftype:

(deftype Foo [a b c]
  Object
  (bar [this x] (+ a b c x)))
(def afoo (Foo. 1 2 3))
(.bar afoo 3) ; >> 9
kanaka
  • 70,845
  • 23
  • 144
  • 140
  • 2
    Note the timestamp. This answer actually came after the one by dnolen on Jan 26, 2012 (about a year later), and this seems much more elegant. – Greg Slepak Jun 25 '13 at 21:16
12
(defprotocol IFoo
  (bar [this x]))

(deftype Foo [a b c]
  IFoo
  (bar [_ x]
    (+ a b c x)))

(def afoo (Foo. 1 2 3))
(bar afoo 3) ; >> 9

Is the idiomatic way to do this.

Matt
  • 74,352
  • 26
  • 153
  • 180
dnolen
  • 18,496
  • 4
  • 62
  • 71
  • Thanks, it looks idiomatic in fact but you can't call from js, (we are building js lib): cljs.user.afoo.bar(3) where we can: cljs.user.x.bar(3) In order to use your idiomatic version from js side one have to call: cljs.user.afoo.cljs$user$IFoo$bar(null, 3) Am I missing something? – Lambder Jan 27 '12 at 10:18
  • @user535149 that's a different question really. If you want to export something use (defn ^:export foo [..] ...) – dnolen Jan 27 '12 at 17:19
  • ^:export works as long as 'something' is a function. I want to export an object whose methods are defined on its prototype. – Lambder Feb 06 '12 at 22:37
  • 1
    That won't be very useful since the methods on the prototype will be namespaced. – dnolen Feb 06 '12 at 22:47
  • So, what would be idiomatic clojurescript way to create an object with constructor, with methods on prototype? Is is the way I proposed in my original question the only one? – Lambder Feb 07 '12 at 14:54