Skip to content

Commit

Permalink
inline defgoalfn, destructured goals
Browse files Browse the repository at this point in the history
  • Loading branch information
tamasjung committed Jan 27, 2024
1 parent 037be6a commit 35a7724
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 79 deletions.
7 changes: 4 additions & 3 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{:deps {org.clojure/core.async {:mvn/version "1.3.618"}}
{:deps {org.clojure/core.async {:mvn/version "1.6.673"}}
:paths ["src"]

:aliases {:test
{:extra-paths ["test"]
:extra-deps { ;org.clojure/clojure {:mvn/version "1.11.0-alpha1"}
:extra-deps {;org.clojure/clojure {:mvn/version "1.11.0-alpha1"}
com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git"
:sha "4e7e1c0dfd5291fa2134df052443dc29695d8cbe"}
org.clojure/test.check {:mvn/version "1.1.1"}}
Expand All @@ -12,7 +12,8 @@

:cljs-test
{:extra-paths ["test-cljs" "test"]
:extra-deps {org.clojure/clojurescript {:mvn/version "1.10.758"}}
:extra-deps {org.clojure/clojurescript {:mvn/version "1.11.60"}
org.clojure/test.check {:mvn/version "1.1.1"}}
:main-opts ["-m" "cljs.main"]}

:release
Expand Down
110 changes: 70 additions & 40 deletions src/maker/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@
[(goal-realisation ctx var1)
(goal-realisation ctx var2)])

(defn goal-meta
[goal-var]
(->> goal-var *meta-fn* (map key) (some #{::multicase})))

(def ^:dynamic *redefinitions* {})

(defn- dependencies
Expand Down Expand Up @@ -131,7 +135,7 @@

(defn multicase?
[goal-var]
(-> goal-var *meta-fn* ::multicase true?))
(-> goal-var goal-meta (= ::multicase)))

(defn- take-until
[pred coll]
Expand Down Expand Up @@ -168,8 +172,7 @@
(def ^:dynamic *goal-var-to-cases-fn* (comp deref #_the_atom deref #_the_var goalvar-to-multi-registry))

(defmulti goal-model (fn [goal-var _]
(when (multicase? goal-var)
::multicase)))
(goal-meta goal-var)))

(defn case-model
[multi-goal-var sorting-state [dispatch-value case-goal-var]]
Expand Down Expand Up @@ -251,7 +254,7 @@

(defn- free-text-to-symbol-chars
[txt]
(subs (str/replace txt #"[^a-zA-Z0-9\*\+\!\-\_\'\?\<\>\=\:]" "-")
(subs (str/replace txt #"[^a-zA-Z0-9\*\+\!\-\_\'\?\<\>\=]" "-")
0 (min 100 (count txt))))

(defn render-case-assignment
Expand All @@ -266,10 +269,10 @@
:as _case-model}]
(let [params (mapv var-to-local-fn used-dep-vars)
;only for debugging purpose, it will be on the stack-trace
fn-name (-> (str dispatch-value
fn-name (-> (str "case-fn"
dispatch-value
"-"
(var-to-local-fn multi-goal-var)
"-fn")
(var-to-local-fn multi-goal-var))
free-text-to-symbol-chars
gensym)
assignments (reduce into []
Expand Down Expand Up @@ -322,7 +325,7 @@

(defmacro with-config
"Makes the first parameter as a configuration goal at compile time to extract the keys.
At runtime it checks if the same keys are present."
At runtime, it checks if the same keys are present."
[configs & body]
;is it less readable if it is (more) hygenic? is it better?
;TODO ^^^
Expand Down Expand Up @@ -382,44 +385,49 @@
`(defn ~(maker-fn-name-with-goal-meta name)
~@(rebuild-args doc params body))))

(defn- build-refers-for
[goal-vars]
(->> goal-vars
(remove #(= (*ns-fn*) (-> % *meta-fn* :ns))) ;FIXME remove those which already have aliases in the (*ns-fn*) too
(mapv #(let [the-name (-> % *meta-fn* :name symbol)]
(list 'require `(quote [~(-> % *meta-fn* :ns *ns-name-fn*)
:refer [~the-name]
:rename ~{the-name (-> (goal-var-goal-local (*ns-fn*) %) with-maker-postfix symbol)}]))))))

(defmacro defgoalfn ;better name? dash or not dash
(defmacro defgoalfn
[name & args]
(let [{:keys [doc params body goal-sym]} (args-map [[:doc string?]
[:params vector?]
[:goal-sym symbol?]]
args)
param-goal-vars (map (partial goal-sym-goal-var (*ns-fn*)) params)
;param-map entry: [param-goal-map param]
param-map (->> (map vector param-goal-vars params)
(into {}))
base-goal-var (goal-sym-goal-var (*ns-fn*) goal-sym)
deps-goal-vars (dependencies base-goal-var)
additional-param-goal-vars (remove (set param-goal-vars) deps-goal-vars)]
`(do
(defgoal ~(vary-meta name assoc ::defgoalfn true)
~@(concat
(rebuild-args doc (->> additional-param-goal-vars
(mapv (partial goal-var-goal-local (*ns-fn*))))
body)
[`(fn ~params
(~(-> goal-sym with-maker-postfix symbol)
~@(mapv #(or (param-map %)
(goal-var-goal-local (*ns-fn*) %)) deps-goal-vars)))])))))
(let [{:keys [doc fn-params goal-params body]} (args-map [[:doc string?]
[:fn-params vector?]
[:goal-params vector?]]
args)]
(if (and fn-params goal-params body)
(let [generated-goal-name (gensym (str name "-goal"))]
`(do
(defgoal ~generated-goal-name
~goal-params
~@body)
(defgoalfn ~@(concat (if doc
[doc]
[])
[name fn-params generated-goal-name]))))
(let [{:keys [doc params body goal-sym]} (args-map [[:doc string?]
[:params vector?]
[:goal-sym symbol?]]
args)
param-goal-vars (map (partial goal-sym-goal-var (*ns-fn*)) params)
param-map (->> (map vector param-goal-vars params)
(into {}))
base-goal-var (goal-sym-goal-var (*ns-fn*) goal-sym)
outer-param-goal-vars (second
(graph/collect-closest-independents dependencies param-map base-goal-var))]

`(defgoal ~(vary-meta name assoc ::defgoalfn true)
~@(concat
(rebuild-args doc (->> outer-param-goal-vars
(mapv (partial goal-var-goal-local (*ns-fn*))))
body)
[`(fn ~(-> (str name '- (string/join "-" params))
free-text-to-symbol-chars
gensym)
~params
(make ~goal-sym))]))))))


(defmacro defgoal?
[name]
(list 'declare (maker-fn-name-with-goal-meta name)))


(defmacro defmulticase
"Defines a multi case goal with its name and the dispatch goal's name."
{:arglists '[goal-name docstring? dispatch-goal-name]}
Expand Down Expand Up @@ -462,3 +470,25 @@
(defgoal ~case-goal ~@args)
~(apply (resolve 'maker.core/register-case) nil nil multi-goal dispatch-value [case-goal]))))

(defmacro destruct-goals
[destructuring-exp from-name]
(loop [[v expr & rem-pairs] (destructure [destructuring-exp from-name])
so-far #{from-name}
res []
substitutions {}]
(if v
(let [param (->> expr
(tree-seq coll? seq)
(some so-far))


goal-def `(defgoal ~v [~(substitutions param param)] ~(if (seq? expr)
(map #(substitutions % %) expr)
(substitutions expr expr)))]
(if (= v param)
(recur rem-pairs so-far res substitutions)
(if (= from-name expr)
(recur rem-pairs (conj so-far v) res (conj substitutions [v expr]))
(recur rem-pairs (conj so-far v) (conj res goal-def) substitutions))))
res)))

63 changes: 55 additions & 8 deletions src/maker/graph.clj
Original file line number Diff line number Diff line change
@@ -1,31 +1,78 @@
(ns maker.graph)
(ns maker.graph
(:require [clojure.set :as set]))

(defn first-topsorted
"Next step of a topological sort of a directed graph.
"Next step of a topological sorting of a directed graph.
Throws exception if the graph contains any cycles."
([successors-fn {:keys [stack stack-set sorted-set]
:as state}]
(loop [sorted-set sorted-set
stack-vec stack
stack stack
stack-set stack-set]
(if (empty? stack-vec)
(if (empty? stack)
[nil state]
(let [v (peek stack-vec)]
(let [v (peek stack)]
(if-let [first-unsorted-successor (->> (successors-fn v)
(remove sorted-set)
first)]
(if (stack-set first-unsorted-successor)
(throw (ex-info "Circular dependency: " {:circular v}))
(recur sorted-set
(conj stack-vec first-unsorted-successor)
(conj stack first-unsorted-successor)
(conj stack-set first-unsorted-successor)))
[v {:stack (pop stack-vec)
[v {:stack (pop stack)
:stack-set (disj stack-set v)
:sorted-set (conj sorted-set v)}]))))))

(defn visited-seq
[children-fn n {:keys [perm-set temp-set] :as state}]

(cond
(perm-set n)
[nil state]

(temp-set n)
(throw (ex-info "circle" n))

:else
(if-let [children (seq (children-fn n))]
(let [next-state (update state :temp-set conj n)
sorted-results (reductions (fn [[_ state] m]
(visited-seq children-fn m state))
(list () next-state)
children)]
(concat (lazy-seq (list (concat (->> sorted-results
(map first)
(apply concat))
(list n))))
(lazy-seq
(-> sorted-results last second
(update :temp-set disj n)
(update :perm-set conj n)
vector))))
[[n] (update state :perm-set conj n)])))

(defn top-sorting
[successor-fn state]
(lazy-seq
(let [[v new-state :as i] (first-topsorted successor-fn state)]
(when (some? v)
(cons i (top-sorting successor-fn new-state))))))
(cons i (top-sorting successor-fn new-state))))))

(defn collect-closest-independents
"Collect recursively those nodes which are not dependent-pred and none of their children are."
[children-fn dependent-pred node]
(let [m (memoize (fn [f a-node]
(if (dependent-pred a-node)
[false #{}]
(let [children (children-fn a-node)
children-results (map (partial f f) children)
rec-independent (every? first children-results)]
[rec-independent
(if rec-independent
#{a-node}
(->> children-results
(map second)
(reduce set/union #{})))]))))]
(m m node)))

0 comments on commit 35a7724

Please sign in to comment.