Extend protocol defined in another namespace in Clojure? - clojure

I have this protocol defined in namespace x:
(ns x
(:require [..]))
(defrecord MyData [something something-else])
(defprotocol MyProtocol
(my-fn [data]))
Now I want to create an implementation of this protocol in another namespace. I've tried doing something like this:
(ns y
(:require [..])
(:import (x MyData MyProtocol)))
(extend-protocol MyProtocol
MyData
(my-fn [data]
; Do something with the data
))
However when I try to execute my-fn like this (from my test case):
(ns y-test
(:require [x :refer [MyData my-fn]]
[...]))
...
(let [data (->MyData ...)]
(my-fn data))
I get the following exception:
java.lang.IllegalArgumentException: No implementation of method: :my-fn of protocol: #'x/MyProtocol found for class: x.MyData
If I move MyProtocol to namespace y it seem to work. What am I missing?
Update
After ayato_p's answer I required the Protocol (in y) instead of importing it but I still get the same error. Moving extend-protocol from y to x resolves the problem.

import is just for Java classes, so you can't import MyProtocol by :import.
Following code works with your record type and protocol.
(ns y
(:require [.. Myprotocol])
(:import (x MyData)))

Related

Importing function from other file in clojure

How can i import a function in closure? Suppose i have two files:
test.clj
test2.clj
I want to use the function from test2 in test.
I have tried the following in test, but this is not working:
(namespace user :require test2)
How am a able to import a function in another file?
Basically want to do `from lib import f in python
In file test.clj:
(ns test
(:require [test2 :as t2]))
(defn myfn [x]
(t2/somefn x)
(t2/otherfn x))
In the above example, t2 is an alias for the namespace test2. If instead you prefer to add specified symbols from the namespace use :refer:
(ns test
(:require [test2 :refer [somefn otherfn]]))
(defn myfn [x]
(somefn x)
(otherfn x))
To refer to all public symbols in the namespace, use :refer :all:
(ns test
(:require [test2 :refer :all]))
(defn myfn [x]
(somefn x)
(otherfn x))
Your namespace syntax is a bit off. I usually refer to this cheat-sheet when I need a reminder.
I think the following is the syntax you are looking for.
;; In test.clj
(ns test
(:require [test2 :refer [some-symbol-to-import]]))

Getting the namespace where a form is called

I would like a macro this-ns such that it returns the namespace of the location where it is being called. For instance, if I have this code
(ns nstest.main
(:require [nstest.core :as nstest]))
(defn ns-str [x]
(-> x (.getName) name))
(defn -main [& args]
(println "The ns according to *ns*:" (ns-str *ns*))
(println "The actual ns:" (ns-str (nstest/this-ns))))
I would expect that calling lein run would produce this output:
The ns according to *ns*: user
The actual ns: nstest.main
What I came up with as implementation was the following code:
(ns nstest.core)
(defmacro this-ns []
(let [s (gensym)]
`(do (def ~s)
(-> (var ~s)
(.ns)))))
It does seem to work, but it feels very hacky. Notably, in the above example it will expand to def being invoked inside the -main function which does not feel very clean.
My question: Is there a better way to implement this-ns to obtain the namespace where this-ns is called?
here is one more variant:
(defmacro this-ns []
`(->> (fn []) str (re-find #"^.*?(?=\$|$)") symbol find-ns))
the thing is the anonymous function is compiled to a class named something like
playground.core$_main$fn__181#27a0a5a2, so it starts with the name of the actual namespace the function gets compiled in.
Can't say it looks any less hacky, then your variant, still it avoids the side effect, introduced by def in your case.
Interesting question. I would never have guessed that your code would output user for the first println statement.
The problem is that only the Clojure compiler knows the name of an NS, and that is only when a source file is being compiled. This information is lost before any functions in the NS are called at runtime. That is why we get user from the code: apparently lein calls demo.core/-main from the user ns.
The only way to save the NS information so it is accessible at runtime (vs compile time) is to force an addition to the NS under a known name, as you did with your def in the macro. This is similar to Sean's trick (from Carcingenicate's link):
(def ^:private my-ns *ns*) ; need to paste this into *each* ns
The only other approach I could think of was to somehow get the Java call stack, so we could find out who called our "get-ns" function. Of course, Java provides a simple way to examine the call stack:
(ns demo.core
(:use tupelo.core)
(:require
[clojure.string :as str]))
(defn caller-ns-func []
(let [ex (RuntimeException. "dummy")
st (.getStackTrace ex)
class-names (mapv #(.getClassName %) st)
class-name-this (first class-names)
class-name-caller (first
(drop-while #(= class-name-this %)
class-names))
; class-name-caller is like "tst.demo.core$funky"
[ns-name fn-name] (str/split class-name-caller #"\$")]
(vals->map ns-name fn-name)))
and usage:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.string :as str]
[demo.core :as core]))
(defn funky [& args]
(spyx (core/caller-ns-func)))
(dotest
(funky))
with result:
(core/caller-ns-func) => {:ns-name "tst.demo.core", :fn-name "funky"}
And we didn't even need a macro!

Implementing a multimethod in separate files in different namespace

I am trying to define a multimethod and its implementation in a separate file. It goes something like this:
In file 1
(ns thing.a.b)
(defn dispatch-fn [x] x)
(defmulti foo dispatch-fn)
In file 2
(ns thing.a.b.c
(:require [thing.a.b :refer [foo]])
(defmethod foo "hello" [s] s)
(defmethod foo "goodbye" [s] "TATA")
And in the main file when I am calling the method I define something like this:
(ns thing.a.e
(:require thing.a.b :as test))
.
.
.
(test/foo "hello")
When I do this I get an exception saying "No method in multimethod 'foo'for dispatch value: hello
What am I doing wrong? Or is it not possible to define implementations of multimethods in separate files?
It is possible. The problem is because thing.a.b.c namespace isn't loaded. You have to load it before using.
This is a correct example:
(ns thing.a.e
(:require
[thing.a.b.c] ; Here all your defmethods loaded
[thing.a.b :as test]))
(test/foo "hello")

Accessing clojure macro via namespace in which it was not defined

I've defined a macro in func.clj:
(ns my-library.func)
(defmacro my-macro [arg]
`(identity ~arg))
and I want to bind it to a variable in core.clj:
(ns my-library.core
(:require [my-library.func :as func])
(def my-macro-core func/my-macro)
so that I can do something like the following:
(ns script.core
(:require [my-library.core :as mlc]))
(mlc/my-macro-core :boring-macro) ; -> :boring-macro
However, I can't do that because macros don't evaluate to a value. The following works:
(ns my-library.core
(:require [my-library.func :as func])
(defmacro my-macro-core [arg]
`(func/my-macro ~arg))
but it's redundant, so I was wondering if there's a cleaner way to do this.
It's a bit of a hack, but it can be done:
(ns my-library.func)
(defmacro my-macro [arg]
`(identity ~arg))
(ns my-library.core
(:require [my-library.func :as func]))
(def ^:macro my-macro-core ##'func/my-macro)
(ns script.core
(:require [my-library.core :as mlc]))
(mlc/my-macro-core :boring-macro)
The trick is to set the new var being defined to the raw function value of the macro being aliased, while setting the :macro metadata flag so the compiler recognizes it and calls it appropriately.
Potemkin's import-vars supports macros:
(require '[potemkin :refer [import-vars]])
(import-vars [my-library.core my-macro])

Don't know how to create ISeq from: clojure.lang.Symbol

I have the following clojure code (trying to write / get entries from redis):
(ns hello.core2)
(ns h hello.core2 (:require [taoensso.carmine :as car]))
(def pool (car/make-conn-pool))
(def spec-server1 (car/make-conn-spec))
(defmacro wcar [& body] `(car/with-conn pool spec-server1 ~#body))`
(defn -main
(wcar (car/ping)
(car/set "foo" "bar")
(car/get "foo")))
I get the following error:
Exception in thread "main" java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol
at clojure.lang.RT.seqFrom(RT.java:487)
at clojure.lang.RT.seq(RT.java:468)
Any idea what im doing wrong?
(defn -main ...): you forgot the argument vector for the function -main; it should be (defn -main [& args] ...).
The problem is probably this:
(ns h hello.core2 (:require [taoensso.carmine :as car]))
There should be only one namespace declaration, like this (so also move the first ns expression):
(ns hello.core2
(:require [taoensso.carmine :as car]))