plumatic / plumbing

Prismatic's Clojure(Script) utility belt

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Outputing maps from graphs

saulshanabrook opened this issue · comments

If I don't understand why this is failing:

search.cli=> (s/set-fn-validation! true)
true
search.cli=> (def v {:thing 10})
#'search.cli/v
search.cli=> (g/graph {:hi (fnk [] v) :there (fnk [hi :- {:thing s/Int}] hi)})

ExceptionInfo {:hi #schema.utils.ErrorContainer{:error (not (map? Any))}}  clojure.core/ex-info (core.clj:4617)

It is rather odd because if I inline the v variable it does work:

search.cli=> (g/graph {:hi (fnk []  {:thing 10}) :there (fnk [hi :- {:thing s/Int}] hi)})
{:hi #object[clojure.lang.AFunction$1 0xdd400a6 "clojure.lang.AFunction$1@dd400a6"], :there #object[clojure.lang.AFunction$1 0x2768009a "clojure.lang.AFunction$1@2768009a"]}

It also works if I explicitly declare that the fnk returns a map:

search.cli=> (g/graph {:hi (fnk _ :- {:thing s/Int} [] l) :there (fnk [hi :- {:thing s/Int}] hi)})
{:hi #object[clojure.lang.AFunction$1 0x18223f27 "clojure.lang.AFunction$1@18223f27"], :there #object[clojure.lang.AFunction$1 0x4d7e4696 "clojure.lang.AFunction$1@4d7e4696"]}

This is intentional -- it's the strict compile time type checking for map
structure I mentioned earlier. Graph errors at compile time if it can't
prove that each node supplies all subkeys bound at the top level by other
nodes. We find this very helpful when constructing nested graphs. The
solution is, as you mention, to provide an explicit output schema for the
function.

Is this problematic for you or just unexpected? Thanks!

On Mon, Feb 29, 2016, 7:44 PM Saul Shanabrook notifications@github.com
wrote:

If I don't understand why this is failing:

search.cli=> (s/set-fn-validation! true)
true
search.cli=> (def v {:thing 10})
#'search.cli/v
search.cli=> (g/graph {:hi (fnk [] v) :there (fnk [hi :- {:thing s/Int}] hi)})

ExceptionInfo {:hi #schema.utils.ErrorContainer{:error (not (map? Any))}} clojure.core/ex-info (core.clj:4617)

It is rather odd because if I inline the v variable it does work:

search.cli=> (g/graph {:hi (fnk [] {:thing 10}) :there (fnk [hi :- {:thing s/Int}] hi)})
{:hi #object[clojure.lang.AFunction$1 0xdd400a6 "clojure.lang.AFunction$1@dd400a6"], :there #object[clojure.lang.AFunction$1 0x2768009a "clojure.lang.AFunction$1@2768009a"]}

It also works if I explicitly declare that the fnk returns a map:

search.cli=> (g/graph {:hi (fnk _ :- {:thing s/Int} [] l) :there (fnk [hi :- {:thing s/Int}] hi)})
{:hi #object[clojure.lang.AFunction$1 0x18223f27 "clojure.lang.AFunction$1@18223f27"], :there #object[clojure.lang.AFunction$1 0x4d7e4696 "clojure.lang.AFunction$1@4d7e4696"]}


Reply to this email directly or view it on GitHub
#117.

Hm, I think what I don't understand is that :hi is not a nested graph, it just happens to return a map. The return value of :hi isn't being interpreted as a graph right?

I though there is a difference between having a naked hashmap in the graph (which means it is a nested graph) and having a leaf that returns a map (which is just like any other return value from a node).

I guess you treat nodes that have arguments that take maps differently, because you assume that that is a subtree, not just a node that returns a map, which makes sense.

How does it know that (fnk _ [] {:thing 1} returns a map with the required keys? Is there a special case for if a fnk returns a naked map? Because if I replace the literal hash-map with the hash-map function it fails to type check ((g/graph {:hi (fnk _ [] (hash-map :thing 1)) :there (fnk [hi :- {:thing s/Int}] hi)}))

On Mon, Feb 29, 2016 at 10:10 PM, Saul Shanabrook notifications@github.com
wrote:

Hm, I think what I don't understand is that :hi is not a nested graph, it
just happens to return a map. The return value of :hi isn't being
interpreted as a graph right?

I though there is a difference between having a naked hashmap in the graph
(which means it is a nested graph) and having a leaf that returns a map
(which is just like any other return value from a node).

That is correct, sorry for the confusion. I meant the opposite in a sense:
when you compile a graph with nesting, we first (recursively) compile each
nested subgraph into an ordinary fnk, and then compile the now-flat outer
layer. So a nested subgraph becomes a fnk, whose output schema will
contain the information about which keys it is guaranteed to produce.

I guess you treat nodes that have arguments that take maps differently,

because you assume that that is a subtree, not just a node that returns a
map, which makes sense.

No, they are treated the same. As I mentioned above, by the time the
outermost graph sees it, a subgraph is equivalent to the fnk it is compiled
into.

How does it know that (fnk _ [] {:thing 1} returns a map with the
required keys? Is there a special case for if a fnk returns a naked map?
Because if I replace the literal hash-map with the hash-map function it
fails to type check ((g/graph {:hi (fnk _ [](hash-map :thing 1)) :there
(fnk [hi :- {:thing s/Int}] hi)}))

Yes, this is a special case for fnks that return map literals:

https://github.com/plumatic/plumbing/blob/master/src/plumbing/fnk/schema.cljx#L157

Does that all make sense? Happy to elaborate if you have questions. If
there are updates to the docs that would help clarify, a PR would be very
welcome. (The primary docs for Graph are this test:
https://github.com/plumatic/plumbing/blob/master/test/plumbing/graph_examples_test.cljx
). Thanks!


Reply to this email directly or view it on GitHub
#117 (comment).

That all makes sense. It was the special case that was tripping me up.