The code below accounts for partials of the same arity. However, there
might be a better way to do this with clojure.reflect:
(defn print-partial [a-fn]
(let [c (class a-fn)
fields (into {} (->> c .getDeclaredFields
(map #(vector (.getName %)
(do (.setAccessible % true)
(.get % a-fn))))))
f (get fields "f")
fname (loop [[ns & namespaces] (all-ns)]
(let [nsmap (into {} (map #(try (vector (deref (second %))
(first %))
(catch Exception e
[::e nil]))
(ns-publics ns)))]
(if-let [fname (get nsmap f)]
#_(symbol (str ns) fname)
(str (ns-name ns) "/" fname)
(if (empty? namespaces)
f
(recur namespaces)))))
args (loop [args ""
arg-num 1]
(if-let [arg (get fields (str "arg" arg-num))]
(recur (str args " " (pr-str arg)) (inc arg-num))
args))]
(str "(partial " fname args ")")))
(defmacro partial* [fname arg0 & args]
`(let [pf# (partial ~fname ~arg0 ~@args)
cpf# (class pf#)]
(defmethod print-method cpf# [o# w#]
(print-simple (print-partial o#) w#))
pf#))
Trying out:
(def p6 (partial* + 1 2 3))
#'user/p6
user=> p6
(partial clojure.core/+ 1 2 3)
user=> (def t7 (partial* * 1 1 7))
#'user/t7
user=> t7
(partial clojure.core/* 1 1 7)
user=> p6
(partial clojure.core/+ 1 2 3)
user=> (def comma-join (partial* clojure.string/join ", "))
#'user/comma-join
user=> comma-join
(partial clojure.string/join ", ")
user=> (p6 100)
106
user=> (t7 5)
35
user=> (comma-join ['a 'b 'c])
"a, b, c"
On Friday, April 25, 2014 2:55:23 PM UTC-7, Greg D wrote:
>
> Got it. The macro, as is, will displace the print method for the same
> arity.
>
> On Friday, April 25, 2014 2:50:34 PM UTC-7, Gary Trakhman wrote:
>>
>> Ah, I think I was mistaken in a detail, but generally correct. Try it
>> with two partials of the same arity.
>>
>>
>> https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L2460
>>
>>
>> On Fri, Apr 25, 2014 at 5:47 PM, Greg D <[email protected]> wrote:
>>
>>> I guess I don't understand the problem, or what is meant by "different
>>> classes". A counter-example would be helpful.
>>>
>>> Further transcript using the macro:
>>> (def p6 (partial* + 1 2 3))
>>> #'user/p6
>>> user=> (class p6)
>>> clojure.core$partial$fn__4194
>>> user=> (def p10 (partial* + 1 2 3 4))
>>> #'user/p10
>>> user=> (class p10)
>>> clojure.core$partial$fn__4196
>>>
>>> user=> p6
>>> (partial + 1 2 3)
>>> user=> p10
>>> (partial + 1 2 3 4)
>>> user=> (p6 100)
>>> 106
>>> user=> (p10 100)
>>> 110
>>>
>>> On Friday, April 25, 2014 2:33:50 PM UTC-7, Gary Trakhman wrote:
>>>
>>>> That's not going to work, all the return classes of partial are the
>>>> same class.
>>>>
>>>>
>>>> On Fri, Apr 25, 2014 at 5:26 PM, Greg D <[email protected]> wrote:
>>>>
>>>>> I don't know if this is considered good Clojure, but you could define
>>>>> a print-method within a macro to set up the normal string representation
>>>>> for the partial function:
>>>>> (defmacro partial* [fname arg0 & args]
>>>>> `(let [pf# (partial ~fname ~arg0 ~@args)
>>>>> cpf# (class pf#)]
>>>>> (defmethod print-method cpf# [o# w#]
>>>>> (if (nil? '~args)
>>>>> (print-simple (str "(partial " '~fname " " (pr-str ~arg0)
>>>>> ")") w#)
>>>>> (print-simple (str "(partial " '~fname " " (pr-str ~arg0)
>>>>> ~@(map #(str " " (pr-str %)) args) ")") w#)))
>>>>> pf#))
>>>>>
>>>>> A transcript of some quick examples:
>>>>> user=> (def p6 (partial* + 1 2 3))
>>>>> #'user/p6
>>>>> user=> p6
>>>>> (partial + 1 2 3)
>>>>> user=> (p6 7)
>>>>> 13
>>>>> user=> (def re-find-foo (partial* re-find #"foo"))
>>>>> #'user/re-find-foo
>>>>> user=> re-find-foo
>>>>> (partial re-find #"foo")
>>>>> user=> (re-find-foo "abcdefooghi")
>>>>> "foo"
>>>>>
>>>>>
>>>>> On Friday, April 25, 2014 9:01:37 AM UTC-7, Matthew DeVore wrote:
>>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> There has been one thing bugging me for a long time that seems worth
>>>>>> it to fix, and I was wondering if anyone else has had the same problem.
>>>>>> I
>>>>>> have enjoyed using Clojure's REPL and embracing a Clojure-style data
>>>>>> model
>>>>>> for my app, where everything is a glorified map or vector and there are
>>>>>> no
>>>>>> private fields. I even have a simple dump feature that tells me the
>>>>>> entire
>>>>>> state of my app <https://github.com/google/hesokuri> that was
>>>>>> ridiculously easy to implement, and that takes advantage of the lack of
>>>>>> black box data structures.
>>>>>>
>>>>>> One thing that doesn't really fit in this paradigm is (ironically)
>>>>>> anonymous functions with closures. For instance, (partial + 42) returns
>>>>>> an
>>>>>> anonymous function, and in the REPL or an app dump, it looks hideous:
>>>>>> #<core$partial$fn__4228 clojure.core$partial$fn__4228@1ee1dea2>
>>>>>>
>>>>>> So I've avoided anonymous functions in my app except when they exist
>>>>>> transiently, and don't appear in the dump (for instance, in (map #(str %
>>>>>> "!") foo)). But sometimes I just can't avoid a long-lived anonymous
>>>>>> function practically. The best solution I've come up with is to
>>>>>> transform
>>>>>> anonymous functions when preparing the application dump. (See the
>>>>>> implementation<https://github.com/google/hesokuri/blob/b60cb7222cfdd672e394ef6f22b80c94278fe3a0/src/hesokuri/see.clj#L35>)
>>>>>>
>>>>>> This makes (partial + 42) look like this:
>>>>>>
>>>>>> {:fn-class clojure.core$partial$fn__4228,
>>>>>> "arg1" 42,
>>>>>> "f" {:fn-class clojure.core$_PLUS_}}
>>>>>>
>>>>>> Which isn't great (I'd like to have filenames and line numbers for
>>>>>> each anon fn, and a nicer name for clojure.core/+), but it's a big
>>>>>> improvement. The function's JVM class and the closured values are
>>>>>> revealed.
>>>>>> It would be nice to implement this natively. Having only passing
>>>>>> familiarity with the Clojure code base, to solve it I think one could:
>>>>>>
>>>>>> - give anonymous functions a .getClosure method which creates a
>>>>>> view of the closure on-demand
>>>>>> - (optional) change their .toString implementation to include
>>>>>> this information
>>>>>> - add logic to clojure.pprint to use the .getClosure method (I
>>>>>> guess "(defmethod clojure.pprint/simple-dispatch
>>>>>> clojure.lang.AFunction
>>>>>> etc...)" ?)
>>>>>>
>>>>>> Another feature that would go nicely with this is smarter equality
>>>>>> semantics for anonymous functions, so that any two anonymous functions
>>>>>> generated at the same point in code with equal closures are equal. This
>>>>>> means if I have a function like this:
>>>>>>
>>>>>> (defn exclaimer [bangs] #(apply str % (repeat bangs "!")))
>>>>>>
>>>>>> then the following would be true: (= (exclaimer 10) (exclaimer 10)),
>>>>>> making functions behave a lot more like values. I would love to have
>>>>>> this
>>>>>> particular feature too, although I'm having trouble coming up with a
>>>>>> non-contrived example.
>>>>>>
>>>>>> I'd like to hear some thoughts on this. Thanks!
>>>>>>
>>>>>> Matt
>>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>> 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
>>>>> ---
>>>>> You received this message because you are subscribed to the Google
>>>>> Groups "Clojure" group.
>>>>> To unsubscribe from this group and stop receiving emails from it, send
>>>>> an email to [email protected].
>>>>>
>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>
>>>>
>>>> --
>>> 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
>>> ---
>>> You received this message because you are subscribed to the Google
>>> Groups "Clojure" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to [email protected].
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
>>
--
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
---
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.