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.

Reply via email to