Looks awesome.

I think we're going the same direction. Myself, I wanted clays to be 
first-class items that can live in Clojure namespaces, and give you all of 
that. The downside is this: if I particular clay is wrong for some 
particular evaluation, you're stuck. The clay is the clay is the clay.

There are a couple ways around this. For testing, with-redefs has its 
normal purpose. You can also use a function unsafe-set-clay!! to force a 
clay's value. But that's heavy handed. More usefully, clays can be built 
lexically, as in

 (let [a-clay (clay :value (... stuff ...) :name something)]
  ... stuff ...)

And passed around

(fire kiln clay-with-args a-lexical-clay)

(defclay clay-with-args
   :args [some-clay]
   :value (blah (?? some-clay)))

This would let you build a computational graph on an as-needed basis.

But still, those are clumsy tools: fine for a few edge cases, but if needed 
often you'd want something built for the task. For your use case, it sounds 
like your approach is better. For the specific applications that led to my 
thinking, the entities were pretty well-defined and their computation 
well-known. Making them top-level named objects seems the right way to go.





On Thursday, May 10, 2012 11:11:15 AM UTC-4, mlimotte wrote:
>
> Hi Jeff,
>
> What do you think about a Map interface for this?
>
> I recently implemented something similar in a project of mine, which I 
> called an 'evaluating-map'.  It's not a Web project, but the pattern is a 
> general one.  In my case, a DSL for specifying a job to run.  I want the 
> DSL writer to have access to a lot of data/logic which can come from a lot 
> of different sources (a "big ball of mud" to use your term).
>
> Like you, the mud-ball could contain values or functions.  These functions 
> can have references to other values in the "ball of mud".  I expanded this 
> to include interpolated strings (e.g. "foo is ${foo}") and collections of 
> values/Strings/functions which are handled recursively.  My code doesn't do 
> anything to manage state, although users are encouraged to provide memoized 
> functions and a helper is provided to assist with this.
>
> Here's an example comparable to your example from the Kiln project.
>
> (def m 
>   {:request "foo"
>    :uri #(build-uri (:request %))   ; an anonymous function works, or
>    :path (lfn [uri] (.getPath uri)) ; use lfn, a helper that returns a fn
>    :dispatch 
>      (lfn [path] (condp = path 
>                    "/remove-user" :remove-user
>                    "/add-user" :add-user
>                    "/view-user" :view-user))
>    :action! action                  ; assuming action is defn'ed elsewhere
>    ... and so on ... })
>
> lfn is the helper that I mentioned-- it pulls it's args as keys from the 
> "ball-of-mud" and returns a memoized fn of those args.
>
> Eventually, you fire it.  Like Kiln, the concept is that you have a bunch 
> of code that sets it up and then at some point you mix in a few seed values 
> and kick it off.  My fire function does a bunch of other stuff, but the 
> relevant part boils down to (-> m (assoc :request req) evaluating-map), 
> which is used like this:
>
> (let [k (-> m (assoc :request req) evaluating-map)
>       result (try 
>                (:action! k)
>                (render-template (:template k) ...other kiln data...)
>                ... catch)]
>   ; because it's a Map, you can do things like
>   (log/debug (select-keys k [:uri :path]))
>   result)
>
> I didn't write support for glazes and cleanup.  I think glazes could be 
> done ring-style.  Cleanup requires some extra thought.  Those are nice 
> features of Kiln.
>
> I think Kiln gives you more control over the execution and state, making 
> things like cleanup easy.  What I like about the Map interface, aside from 
> the convenience of being able to use standard collection functions (merge, 
> select-keys, dissoc, etc) is that you can construct the map from many 
> different sources.  I.e. you can merge maps which are constructed 
> dynamically at different points in your flow.  This was important for my 
> use-case, since DSL users are writing code that my core code knows nothing 
> about.  Using the example above, a subsequent user could replace the :uri 
> fn:
>
> (merge m {:uri (lfn [request] (some-other-build-fn request))}) 
>
> This new function would then be the input for the :path function.
>
> My code for this abstraction isn't isolated, but you can see it in context 
> of another project 
> here<https://github.com/TheClimateCorporation/lemur/blob/master/src/main/clj/lemur/evaluating_map.clj>
> .
>
> Anyway, I like the project and thanks for sharing it.
>
> Marc 
>
>
>

-- 
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

Reply via email to