Skip to content

Instantly share code, notes, and snippets.

@Hendekagon
Last active November 20, 2019 18:53
Show Gist options
  • Save Hendekagon/27ab6326e4a5126a793b09e936810327 to your computer and use it in GitHub Desktop.
Save Hendekagon/27ab6326e4a5126a793b09e936810327 to your computer and use it in GitHub Desktop.
Clojure namespace declaration by functions experiment
(fn [params]
{
:namespace-name 'a
:requires []
:defs
'{
x (fn [y] (str "a's " y))
}})
(fn [params]
{
:namespace-name 'b
:requires []
:defs
'{
x (fn [y] (str "b's " y))
}})
(fn [{{:syms [d]} :requires}]
{
:namespace-name 'c
:requires [[d :as 'd]]
:defs
'{
y (fn b [x] (d/x x))
}})
(ns nsfn
(:require
[clojure.string :as s]
[clojure.java.io :as jio]))
(declare require*)
(defn ns*
"
Define a namespace with name a-ns
(or aka a-ns if given in params)
with the given params
"
[params a-ns]
(let [cns *ns*
path-cljc (str (s/replace (name a-ns) #"\." "/") ".cljc")
ns-fn (eval (read-string (slurp (jio/resource path-cljc))))
{:keys [namespace-name defs requires]} (ns-fn params)
the-ns (or (get-in params [:aka a-ns]) namespace-name)
aliases (map last (filter (fn [r] (filter :as r)) requires))
]
(in-ns the-ns)
(refer-clojure)
(when (not-empty aliases)
(apply ns-unalias the-ns aliases))
(when (not-empty requires)
(apply require* params requires))
(doseq [[sym body] defs]
(eval `(def ~sym ~body)))
(in-ns (ns-name cns))))
(defn require* [params [a-ns as aliaz]]
(ns* params a-ns)
(alias aliaz (or (get-in params [:aka a-ns]) a-ns)))
@Hendekagon
Copy link
Author

Hendekagon commented Nov 9, 2019

This is a quick & rough experiment with creating namespaces
using functions to create them which take parameters so that
one can make alternative implementations of namespaces

I am not suggesting this a good way to achieve this functionality!

It's just a sketch of what I'm thinking about today

(require :reload '[nsfn :as n])

Require namespace c as bc, creating the namespace b.c
-- an implementation of c where c's alias d is aliased to namespace b

(n/require* {:requires '{d b} :aka '{c b.c}} '[c :as bc])

(bc/y 6)
=> "b's 6"

Require namespace c as ac, creating the namespace a.c
-- an implementation of c where c's alias d is aliased to namespace a

(n/require* {:requires '{d a} :aka '{c a.c}} '[c :as ac])

(ac/y 6)
=> "a's 6"

Namespaces a and b both provide the same API, 1 function x
which c uses via its alias d. We can switch the namespace aliased to d
with the parameters sent to the function which creates the namespace c

One can imagine other parameters being useful in the creation of a namespace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment