在 2014年4月25日星期五UTC-7下午12时16分22秒,Alex Miller写道:
>
>
> On Friday, April 25, 2014 1:23:49 PM UTC-5, Matthew DeVore wrote:
>>
>> Thanks for pointing out the ticket, but based on the Description, it 
>> falls short of what I need. It doesn't have any additional information that 
>> I can't deduce from looking at the code, in other words, the value of the 
>> items in the closures. So while it makes toString prettier, it's not 
>> informative.
>>
>
> That ticket is not at an end state - it could conceivably be updated to 
> include additional information for anonymous functions. I am suggesting 
> that as a place you could contribute. I do not know what level of changes 
> would be acceptable to Rich in this area. I think I can safely guess that 
> any change that made Clojure slower would not be considered in mainline 
> Clojure (but might be a candidate for some developer-friendly alternate 
> build, which is a possibility). 
>
Good point about the performance concern. It will definitely create more 
byte-code for every anonymous function, so it may not be wise to make it 
the default behavior.

I'm starting to think that I can just implement this with some macro magic 
(i.e. fn*). I just need to figure out how to determine what symbols inside 
an fn* body are arguments, and which are not (maybe using symbol 
metadata?). After that, reify a class that is an AFunction but with an 
extra method to get the closure map. Finally, just add the proper hooks for 
pprint and print-method.
 

>  
>
>> About impurity - it depends on where the impurity comes from. 
>>
>
> Functions in Clojure are not values. In languages like Haskell, it may be 
> possible to leverage reasoning around substitution and pure functions but 
> Clojure has no way to even determine that it is in such a case. If you want 
> to compare functions, then you should describe the invocation with Clojure 
> data so it can be compared like any other data and defer turning it into a 
> function invocation to the point where that needs to occur. 
>
The only assumption that has to be made to treat a function as a value is 
what about that function is part of the value. I'm arguing that the lexical 
location of the function in code, and the values of all the closured 
symbols tell you everything about the function that can be otherwise 
observed by invoking it, so that is all you need to compose its value. If a 
function is first-class, why can it not be a value as well? Considering 
them to not be non-value seems arbitrary and complex.

When using dynamic languages, I like APIs with fewer and shorter words. It 
leaves less room for typos and requires less memorization. Why force people 
to convert a value to a function when one thing can be both?

An example of when I wanted to do this was when I wrote a function to read 
a directory listing (tree object) from a Git database. It returned a 
sequence of vectors that look like this:
["40000" "filename.txt" "SHA1HASH"]

The first item is the object type which identifies it as a regular file. 
Now what if I want to read filename.txt? I need info about the repo 
(location on disk), the file type ("40000") and the hash. In the strategy 
you describe, I'll have to do this:

(def repo (comment this is defined elsewhere))

(for [[type name hash] (read-tree repo tree-hash)
      :when (= name "filename.txt")]
  (read-object repo type hash))

If I decided to return a function from read-tree, I have a simpler API:

(for [[_ name _ read-obj-fn] (read-tree repo tree-hash)
      :when (= name "filename.txt")]
  (read-obj-fn))

I like the second one because I have fewer functions to memorize, and it is 
less error prone because I'm never going to pass the wrong repo or type to 
read-obj-fn, whereas I may do it when I call read-object. If I had value 
semantics for functions also, I would be able to unit test this very 
elegantly, because I can just do:

(is (= (read-tree repo hash)
  [["40000" "filename.txt" "SHA1HASH" (new-read-obj-fn repo "40000" 
"SHA1HASH")]
   ...])

As long as (new-read-obj-fn) is used by the app code as well as the test 
case to create the reader function.

 
>
>> Is it from something like an (atom) value in the closure, that changes 
>> when you call the function? In that case, the functions won't be equal 
>> because (= (atom 0) (atom 0)) is false, as it should be. 
>>
>
>> Is it from something where the equality changes based on the state of the 
>> object, such as a mutable HashMap in the closure? In that case, the 
>> equality of the function will follow that of underlying Java objects: the 
>> functions are equal when they are interchangeable, but unequal when their 
>> states get out of sync. The equality semantics of the objects in the 
>> closure are reflected in the function itself.
>>
>> Is the impurity coming from something in the environment, like the 
>> filesystem? In that case, the two functions will be equal, since they don't 
>> store the state in their closures, which makes sense, since the functions 
>> are interchangeable.
>>
>> 在 2014年4月25日星期五UTC-7上午11时01分06秒,Alex Miller写道:
>>>
>>> You might be interested in this ticket (
>>> http://dev.clojure.org/jira/browse/CLJ-1278<http://www.google.com/url?q=http%3A%2F%2Fdev.clojure.org%2Fjira%2Fbrowse%2FCLJ-1278&sa=D&sntz=1&usg=AFQjCNF56KKjllTJ7mHNbmI4cu-U8QGK-g>)
>>>  
>>> of which this is perhaps one special case. 
>>>
>>> I don't know that I necessarily would want the equality semantics; at 
>>> least in the case of impure functions the equality does not hold.
>>>
>>> On Friday, April 25, 2014 11:01:37 AM UTC-5, 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 clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
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 clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to