Hi All, Just following up, I've been contemplating the idea of dynamic values/time-varying state a lot in the context of my previous email. If something that is deref-able (IDeref) is considered something that is time-varying, then it also leaks that what uses it is impure.
If IDeref becomes something representing a time-varying value, then we could have something like: (def input (r!*! read-line)) (defn process [src] (loop [v @src] (when v (println "Read: " v) (recur @src)))) With (process input), this ends up blocking on the deref of input, awaiting on the read-line. If on the other hand, we don't use a IDeref, but a 0-arity function: (defn process [func] (loop [v (func)] (when v (println "Read: " v) (recur (func))))) We get the notion with (process read-line) that func is impure and returns different values over time, but it's not as visible IMO as compared to @src. Extending this line of thought, I then thought about core.async's channels as just another source of values over time. For example, if channels were deref-able, then this: (*go* (*while* true (*let* [v (<! ch)] (println "Read: " v)))) could be rewritten as: (*go* (*while* true (*let* [v @ch] (println "Read: " v)))) If the code above was not written in a go block, then it would look very similar to the process function earlier. It would also mean you could pass in ch as a channel, or pass in the (r!*! read-line), or pass in a deref-able such as: *(*defn seq-iter *[*s*]* *(let* *[*head *(**atom* s*)]* *(*reify IDeref *(**deref* *[*_*]* *(let* *[[*x *&* xs*]* *@*head*]* *(**reset!* head xs*)* x*)))))* (process (seq-iter ["a" "b" "c")) And the process code would work with all three sources of time-varying values. (The last could be useful for mocking changes in values over time). It also doesn't matter to the process function whether the passed in IDeref is an atom, ref, channel, or some other source of value; or whether it blocks or not. The code then becomes generalized to code that works with mutable data, and we can pass in arbitrary data sources. Something I both like and dislike about the !*! notation is that it is highly visible in the code. I don't know if it is as clear as something like the conventions of *earmuffs* on a var to show something dynamic is used, but at least the code using it signifies: this function and code is not like the others, it is impure and deals with mutable values. Using @ also denotes working with something involving change and time, which seems to make the @ and r!*! blocks have a natural fit. On the one hand, I'm not sure if generalizing IDeref to time-varying values, and the things mentioned above, would make things more complicated or less. On the other hand, IDeref also has multiple meanings as it is (i.e. get me the current value in time of a thing, get me the one and only value but when it's ready). This is to just to say, I'm not sure about all this yet, still just very curious. Thanks! steven On Wednesday, December 3, 2014 12:21:54 AM UTC-5, Steven Yi wrote: > > Hi All, > > I was working on a music notation issue with my music libraries Pink > and Score and came upon a solution I thought was curious. It certainly > solved a real problem I had with delayed function calls, and I thought > maybe others might find a use for it too. > > The scenario I had is that in writing events, I needed to find a way > to have certain arguments be dynamic at the time of application of the > function, rather than the time of event creation. For example: > > [fm 2.5 0.3 (env [0.0 500 0.5 1000]) 0.3] > > when processed by Score and Pink, would yield an event data structure > where the 3rd argument to the fm function would be a stateful function > generated by (env ...). That would be fine if that the event was used > once, but if the event was reused (i.e. I want to listen to that block > of score again...), the function returned by (env) would have already > been processed. > > The code I came up with is at [1]. It allowed so that I could use: > > [fm 2.5 0.3 (!*! env [0.0 500 0.5 1000]) 0.3] > > and everytime the fm was applied, the code within the !*! was run at > apply time. This ends up using the apply!*! function from [1] instead > of apply to resolve that argument when applying the fm function. > > I found this curious as it meant that I didn't have to write a new > wrapper function just to dynamically create arguments to call the fm > function, such as: > > [#(fm 0.3 (env [0.0 500 0.5 1000]) 0.3) 0.25] > > which seemed a bit more noisy and less clear to the intent of the event > code. > > I tried to think of a more general scenario where this might be > useful. I came up with two examples, the first was composing a > logging function: > > user=> (def logger (partial apply!*! println "[" (!*! #(str (new > java.util.Date))) "]")) > #'user/logger > user=> (logger "testing2") > [ Tue Dec 02 22:22:44 EST 2014 ] testing2 > nil > user=> (logger "testing3") > [ Tue Dec 02 22:22:50 EST 2014 ] testing3 > > Not too exciting, I know. On the other hand, I thought about FRP, and > came up with: > > user=> (defn r!*! [func & args] > #_=> (reify IDeref > #_=> (deref [_] (apply!*! func args))) > #_=> ) > #'user/r!*! > user=> (def a (atom 45)) > #'user/a > user=> (def b (r!*! * (!*! deref a) 10)) > #'user/b > user=> @a > 45 > user=> @b > 450 > user=> (reset! a 50) > 50 > user=> @b > 500 > user=> (def c (r!*! + (!*! deref b) 6)) > #'user/c > user=> @c > 506 > user=> (reset! a 10) > 10 > user=> @a > 10 > user=> @b > 100 > user=> @c > 106 > > I thought the notation of the r!*! reactive cells above looked nice to > write, and using IDeref captured a bit of the idea of working with > time-varying values. That could also be a little clearer with: > > user=> (def sig (partial !*! deref)) > #'user/sig > user=> (def c (r!*! + (sig b) 6)) > #'user/c > user=> @c > 106 > > The code at [1] is pretty small, but seemed to open up a number of > interesting ways to express things that I hadn't thought about before > in terms of apply-time of functions. Any feedback/comments/advice > very welcome! > > Thanks! > steven > > [1] - > https://github.com/kunstmusik/pink/blob/master/src/pink/util.clj#L9-L29 > -- 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.