Modified doto

11 views
Skip to first unread message

mb

unread,
Oct 21, 2008, 8:41:54 AM10/21/08
to Clojure
Hi,

recently I ran in the a limitation of doto, that it only invokes
methods. However piping the object with -> does not work
also, since it's semantics are more like .. .

I'd like to propose the following chimera of doto and ->.

(defmacro doto->
[obj & forms]
(let [objx (gensym "obj__")]
`(let [~objx ~obj]
(do
~@(map (fn [f]
(if (seq? f)
`(~(first f) ~objx ~@(rest f))
`(~f ~objx)))
forms))
~objx)))

It allows the full support of doto via the dot notation of
methods. And it supports on the other hand other functions
not only methods. One example is the new miglayout
interface in clojure-contrib.

(doto-> (new JFrame "Hello, World!")
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(miglayout SomeChild :AConstraint MoreChildren ...))

Any thoughts?

Sincerely
Meikel

Chouser

unread,
Oct 21, 2008, 10:03:02 AM10/21/08
to clo...@googlegroups.com
On Tue, Oct 21, 2008 at 8:41 AM, mb <m...@kotka.de> wrote:
>
> (doto-> (new JFrame "Hello, World!")
> (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
> (miglayout SomeChild :AConstraint MoreChildren ...))
>
> Any thoughts?

Beautiful.

I've found in practice that using doto seems okay at first, but I
commonly have to insert a function call eventually, and end up having
to change a lot of existing code to get rid of the doto's. This has
been painful enough that I've pretty much stopped using doto in code
that's not just one-off stuff in a repl.

--Chouser

Timothy Pratley

unread,
Oct 21, 2008, 10:20:19 AM10/21/08
to Clojure


> Any thoughts?

Awesome! :)

mb

unread,
Oct 21, 2008, 10:30:28 AM10/21/08
to Clojure
Hi,

On 21 Okt., 14:41, mb <m...@kotka.de> wrote:
> (defmacro doto->
The name is actually also up to discussion. doto is already
in use and this change is incompatible to "legacy" code.
I couldn't come up with a good alternative...

Sincerely
Meikel

Stephen C. Gilardi

unread,
Oct 21, 2008, 11:05:01 AM10/21/08
to clo...@googlegroups.com
On Oct 21, 2008, at 8:41 AM, mb wrote:

I'd like to propose the following chimera of doto and ->.

(defmacro doto->
 [obj & forms]
 (let [objx (gensym "obj__")]
   `(let [~objx ~obj]
      (do
        ~@(map (fn [f]
                 (if (seq? f)
                   `(~(first f) ~objx ~@(rest f))
                   `(~f ~objx)))
               forms))
      ~objx)))

Is the ".." aspect of it the "automatically make a list if it's not one" part?

Along the lines of your example, one could do:

(doto-> (JFrame. "Hello Frame")
              (miglayout (JLabel. "Hello Label"))
              .pack
              (.setVisible true))

I like it!

It seems a key thing to remember here is that while this does return a useful value, it's primarily a construct for side effects. We're calling these Clojure functions because they're compositions of calls that have side effects either on the state of obj or on the system as a whole (e.g., showing a window). We're throwing away the values produced by the individual calls. This is in contrast to "->" where each call's result is consumed by the next function in the thread.

As for the name, this operation seems a lot more like "doto" than "->" to me. One option is "doto" in a namespace other than "clojure". Prefix and suffix "." are both reserved to Clojure, so "doto." is out. "do-for-side-effects" seems slightly wordy. "goto" is considered harmful. I think if it can't be "doto", "doto->" is a fine name.

Cheers,

--Steve

CuppoJava

unread,
Oct 21, 2008, 11:10:06 AM10/21/08
to Clojure
If I understand the macro correctly, it takes an argument, and then
inserts it as the second element into all of the following lists
right?
How about the name "with"?

(with obj
(. doSomething)
(. doSomethingElse)
(print stdOut))

I think i'm stealing it from Ruby, but i'm not sure. It's been a long
time.

J. McConnell

unread,
Oct 21, 2008, 11:13:59 AM10/21/08
to clo...@googlegroups.com
I like "with", that's what JavaScript uses IIRC.

- J.

Chouser

unread,
Oct 21, 2008, 11:24:03 AM10/21/08
to clo...@googlegroups.com
On Tue, Oct 21, 2008 at 11:13 AM, J. McConnell <jdo...@gmail.com> wrote:
>
> I like "with", that's what JavaScript uses IIRC.

I think JavaScript's "with" means something slightly different. I
don't remember about Ruby.

But as Stephen pointed out, this is only useful when the methods (or
functions) have side-effects -- their return values are thrown away.
This explains the use of "do" in the original name, and is a good
reason to keep "do" in the new name.

I don't see much wrong with "doto->", though "do-with" or "do->" might
be okay. I'd probably vote against "do-unto-others-as"

--Chouser

mb

unread,
Oct 21, 2008, 11:31:36 AM10/21/08
to Clojure
Hello Stephen,

On 21 Okt., 17:05, "Stephen C. Gilardi" <squee...@mac.com> wrote:
> Is the ".." aspect of it the "automatically make a list if it's not  
> one" part?
This is actually a -> aspect. What I meant was:
(.. x (getModel) (getRoot) (state))
is equivalent to
(-> x .getModel .getRoot .state)

That is the reason, why (-> frame (.method1 ...) (.method2 ...))
doesn't work in the given examples. The methods most likely
don't return frame...

> (doto-> (JFrame. "Hello Frame")
>                (miglayout (JLabel. "Hello Label"))
>                .pack
>                (.setVisible true))
This is exactly my first use case. :)

> It seems a key thing to remember here is that while this does return a  
> useful value, it's primarily a construct for side effects. We're  
> calling these Clojure functions because they're compositions of calls  
> that have side effects either on the state of obj or on the system as  
> a whole (e.g., showing a window). We're throwing away the values  
> produced by the individual calls. This is in contrast to "->" where  
> each call's result is consumed by the next function in the thread.
Yes. This is intended as an augmented (or improved or whatever) doto.
It's purely for side effects, but very handy for object creation,
which
needs to call several methods on the same object.

For functional uses -> is almost perfect. Hmm... When we currently
talk about it... How about this:

(defmacro xxx->
([x form] (if (seq? form)
(let [[hd tl] (split-with #(not= '<> %) form)]
(if tl
`(~@(concat hd (cons x (rest tl))))
`(~(first form) ~x ~@(rest form))))
(list form x)))
([x form & more] `(-> (-> ~x ~form) ~@more)))

Disclaimer: this is only a rough idea!!! Let alone a correct
implementation.

The idea is to provide a way to let the piped argument be someting
else than the first one.

(xxx-> "Hello" (apply str <> [", " "World!"])) gives "Hello, World!".

The <> is used to mark the "hole" where the value is to be inserted.
Should this be only replaced in the top expression? Or also in
sub expressions? Without zipper in boot.clj? Is this a good idea
in the first place?

Acknowledgement: <> is stolen from the cut notation of some SRFI.

Sincerely
Meikel

mb

unread,
Oct 21, 2008, 11:35:30 AM10/21/08
to Clojure
Hi,

On 21 Okt., 17:24, Chouser <chou...@gmail.com> wrote:
> I don't see much wrong with "doto->", though "do-with" or "do->" might
> be okay.  I'd probably vote against "do-unto-others-as"
I would vote for do-with.

Sincerely
Meikel

J. McConnell

unread,
Oct 21, 2008, 11:36:20 AM10/21/08
to clo...@googlegroups.com

+ 1

Rastislav Kassak

unread,
Oct 21, 2008, 12:19:59 PM10/21/08
to clo...@googlegroups.com
I vote for "do-with" too.
I used to upgrade my Ruby runtime with Ola Bini's Kernel::with.
It could be used exactly the same way as proposed do-with. However,
do-with is more idiomatic name, IMHO.

RK

CuppoJava

unread,
Oct 21, 2008, 12:33:33 PM10/21/08
to Clojure
+1 for do-with for me as well.

Thank you for considering adding this to Clojure. I actually wrote
this macro myself, but I've always thought it was in the API already
and I just didn't know what it was called.

Chouser

unread,
Oct 21, 2008, 1:08:44 PM10/21/08
to clo...@googlegroups.com
On Tue, Oct 21, 2008 at 11:31 AM, mb <m...@kotka.de> wrote:
>
> (xxx-> "Hello" (apply str <> [", " "World!"])) gives "Hello, World!".
>
> The <> is used to mark the "hole" where the value is to be inserted.

I wrote something like this too. I called mine >>_ and used _ as the
insert mark.

Here's my implementation:

(defmacro >>_ [& exprs]
(list 'let (apply vector (mapcat (fn [expr] (list '_ expr)) exprs)) '_ ))

user=> (>>_ "Hello" (apply str _ [", " "World!"]))
"Hello, World!"

I used it a couple times after first writing it, but have since failed
to find much use for it. I guess I wouldn't really recommend it.

--Chouser

Sean Spencer

unread,
Oct 21, 2008, 1:20:36 PM10/21/08
to clo...@googlegroups.com
I also thought it was in the language and I just didn't know the name.  Good to see it added!

And "do-with" sounds like a good name to me.

Martin DeMello

unread,
Oct 21, 2008, 3:01:08 PM10/21/08
to Clojure
On Oct 21, 5:41 am, mb <m...@kotka.de> wrote:
>
> It allows the full support of doto via the dot notation of
> methods. And it supports on the other hand other functions
> not only methods. One example is the new miglayout
> interface in clojure-contrib.

Thanks! That's going to be really useful.

martin

mb

unread,
Oct 21, 2008, 4:02:47 PM10/21/08
to Clojure
Hello,

On 21 Okt., 19:08, Chouser <chou...@gmail.com> wrote:
> Here's my implementation:
>
> (defmacro >>_ [& exprs]
> (list 'let (apply vector (mapcat (fn [expr] (list '_ expr)) exprs)) '_ ))
Now this is a nice idea.

> I used it a couple times after first writing it, but have since failed
> to find much use for it. I guess I wouldn't really recommend it.
I often encountered the case, that I want to do an apply in the
-> or eg. some filter or map, which also does not work. But maybe
you are right, and this is a sign that the code is bad.

Nevertheless here a combined version, which does not need to
specify the _ all the time.

(defmacro >>
[x & forms]
`(let ~(apply vector
'_ x
(mapcat (fn [form]
(if (seq? form)
(if (some #(= '_ %) form)
(list '_ form)
(list '_ (list* (first form) '_ (rest
form))))
(list '_ (list form '_))))
forms))
~'_))

Sincerely
Meikel

Rich Hickey

unread,
Oct 23, 2008, 10:53:24 AM10/23/08
to Clojure
I'd rather enhance doto to do this and not add another variant. The
break would be that current (doto x (foo 42)) would have to become
(doto x (.foo 42)).

Any thoughts on this as part of the upcoming bit of breaking changes?

Rich

Chouser

unread,
Oct 23, 2008, 11:39:24 AM10/23/08
to clo...@googlegroups.com
On Thu, Oct 23, 2008 at 10:53 AM, Rich Hickey <richh...@gmail.com> wrote:
>
> I'd rather enhance doto to do this and not add another variant. The
> break would be that current (doto x (foo 42)) would have to become
> (doto x (.foo 42)).
>
> Any thoughts on this as part of the upcoming bit of breaking changes?

Under most circumstances you'd get compile errors up front, I think,
and those would be simple to fix.

Wouldn't bother me.

--Chouser

Stephen C. Gilardi

unread,
Oct 23, 2008, 11:57:47 AM10/23/08
to clo...@googlegroups.com

On Oct 23, 2008, at 10:53 AM, Rich Hickey wrote:

Any thoughts on this as part of the upcoming bit of breaking changes?

I think it would be a very useful change. I'm in favor.

--Steve

mac

unread,
Oct 24, 2008, 7:03:08 AM10/24/08
to Clojure
+1, would be a very nice change.

/mac

Christian Vest Hansen

unread,
Oct 24, 2008, 7:15:22 AM10/24/08
to clo...@googlegroups.com

In addition, I think having to always use dot-something for methods is
good consistency.

+1.

>
> Rich
>
> >
>

--
Venlig hilsen / Kind regards,
Christian Vest Hansen.

CuppoJava

unread,
Oct 24, 2008, 9:02:13 PM10/24/08
to Clojure
On non-backwards compatible language changes in general, isn't it
trivial to write a source-code converter?
Especially given the ease of Clojure's macro system, all you would
need is a systematic find and replace on any code that uses the
current doto right?
That would save the manual labor of having to updating all the current
libraries.

Or am I missing something vital?
-Patrick

V.Quixote

unread,
Oct 25, 2008, 5:27:08 AM10/25/08
to Clojure
I'd like some version of doto that works on bare Classes

mac

unread,
Oct 27, 2008, 5:18:29 PM10/27/08
to Clojure


On Oct 25, 10:27 am, "V.Quixote" <woova...@student.otago.ac.nz> wrote:
> I'd like some version of doto that works on bare Classes

(defmacro sdoto
"Version of doto for use with static methods"
[x & methods]
`(do
~@(map (fn [m] (list '. x m))
methods)
~x))

Here you go. sdoto for "static doto".

/mac

Rich Hickey

unread,
Nov 19, 2008, 10:22:28 AM11/19/08
to Clojure


On Oct 23, 9:53 am, Rich Hickey <richhic...@gmail.com> wrote:
> On Oct 21, 10:30 am, mb <m...@kotka.de> wrote:
>
> > Hi,
>
> > On 21 Okt., 14:41, mb <m...@kotka.de> wrote:> (defmacro doto->
>
> > The name is actually also up to discussion. doto is already
> > in use and this change is incompatible to "legacy" code.
> > I couldn't come up with a good alternative...
>
> I'd rather enhance doto to do this and not add another variant. The
> break would be that current (doto x (foo 42)) would have to become
> (doto x (.foo 42)).
>

Rev. 1111 makes this (breaking) change, hopefully the last breakage
pre 1.0.

I've updated the ants.clj file here, and would appreciate it if people
could touch up the wiki and any other public demos.

Thanks,

Rich
Reply all
Reply to author
Forward
0 new messages