On Fri, Jun 17, 2011 at 7:32 PM, Alex Baranosky
<[email protected]> wrote:
> What is the best way to remove the duplication in these two functions?:
>
> (defn- next-day-of-week-in-future [day-num]
>   (find-first #(= day-num (.. % dayOfWeek get)) (today+all-future-dates)))
>
>
> (defn- next-day-of-month-in-future [day-of-month]
>
>   (find-first #(= day-of-month (.. % dayOfMonth get))
> (today+all-future-dates)))
>
>
>
> ;; Here (today+all-future-dates) is a seq of DateMidnight objects from joda
> time
>
>
> I tried to do something like this - but it failed due to my lack of
> understanding:
>
> (defn- next-day-of-period [period day-of-period]
>   (find-first #(= day-of-period (.. % period get))
> (today+all-future-dates)))
>
> (def ^{:private true} next-day-of-week-in-future (partial next-day-of-period
> 'dayOfWeek))
>
>
> (def ^{:private true} next-day-of-month-in-future (partial
> next-day-of-period 'dayOfMonth))
>
>
>  No matching field found: period for class org.joda.time.DateMidnight
>
>
> Is this kind of thing possible?  Are the interop macros or #() macro
> interfering? I'd love to understand this bit a bit better.

It's possible, but not the way you did it. Your problem is that
dayOfMonth and dayOfWeek are Java methods rather than first-class
functions.

One possibility is to make your new function a macro; it will work
then, by replacing "period" with dayOfMonth or dayOfWeek at
macroexpansion time.

The other is to replace the interop call (.. % period get) with (.get
(period %)) and making two more functions:

(defn day-of-month [x] (.dayOfMonth x))

(defn day-of-week [x] (.dayOfWeek x))

whereupon (next-day-of-period day-of-week foo) will work, for instance.

The macro is probably preferable in this particular instance since it
directly lets you plug in any valid period method name without having
to write more wrapper functions like the above two for each one. Here
it is:

(defmacro next-day-of-period [period day-of-period]
  `(find-first #(= ~day-of-period (.. % ~period get))
    (today+all-future-dates)))

=> (macroexpand-1 '(next-day-of-period dayOfMonth 11))
(user/find-first (fn* [p1__646__647__auto__] (clojure.core/= 11 (..
p1__646__647__auto__ dayOfMonth clojure.core/get)))
(user/today+all-future-dates))

Don't worry about the clojure.core/get cruft. Clojure doesn't care
about the namespace part of a symbol used in Java interop, only the
name part. Observe:

=> (. (WeakReference. 42) clojure.core/get)
42

Same result as with plain 'get. Heck,

=> (clojure.core/.get (WeakReference. 42))
42

acts the same as plain (.get (WeakReference. 42))! Though
(.clojure.core/get ...) blows up with a no such namespace exception.

In fact:

=> (macroexpand-1 '(quux/.foo bar))
(. bar foo)

shows that (.foo bar) is transformed to (. bar foo) (and the namespace
part of .foo, if any, dropped) by the macroexpander -- (.meth) symbols
seem to be treated by the compiler as an open-ended set of implicit
macros that expand to the (. bar foo) form of interop. Interestingly,

=> (macroexpand-1 '(. bar quux/foo))
(. bar quux/foo)

so macroexpansion isn't where the namespace gets stripped if it's
already in (. bar foo) form; the compiled code must simply ignore
everything but the name part of the symbol in the method name position
(second argument to the dot special operator).

-- 
Protege: What is this seething mass of parentheses?!
Master: Your father's Lisp REPL. This is the language of a true
hacker. Not as clumsy or random as C++; a language for a more
civilized age.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to