Importing function from other file in clojure - 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]]))

Related

Is there a way to get Clojure file's source when a namespace provided?

There are some functions that read source code of a function like: source and source-fn.
Is there any way or function that returns source code of the Clojure file when a namespace is provided?
Such as: (all-source 'my-ns)
Returns such as:
(ns my-ns
(:require [kezban.core :refer :all]
[leiningen.c.util :as util]))
(defn my-fn
[]
)
...
I think I found a way(it works if the ns has at least one var):
(defn source-clj
[ns]
(require ns)
(some->> ns
ns-publics
vals
first
meta
:file
(.getResourceAsStream (RT/baseLoader))
IOUtils/toString))

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

Extend protocol defined in another namespace in 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)))

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

Trouble referring to defrecord symbols in Clojure unit test?

Consider the following leiningen project core.clj file
(ns records.core)
(defn hello [] "hello")
(defprotocol my-sequence
(add [seqq item]))
(defrecord my-vector [coll]
my-sequence
(add [_ item] (conj coll item)))
I can compile this and test it in the REPL as follows:
records.core> (hello)
"hello"
records.core> (add (my-vector. []) 42)
[42]
But when I transcribe this into the leiningen unit-test file as follows:
(ns records.core-test
(:require [clojure.test :refer :all]
[records.core :refer :all]))
(deftest a-test
(testing "adding to a my-vector"
(is (= (hello) "hello"))
#_(is (= [42] (add (my-vector. []) 42)))))
The first test succeeds, showing that symbol hello is correctly moved into the records.core-test namespace, but the test of my-vector throws a compiler error (remove the #_ on the second line above):
clojure.lang.Compiler$CompilerException: java.lang.IllegalArgumentException:
Unable to resolve classname: my-vector, compiling:
(.../records/test/records/core_test.clj:8:22)
This does not seem to be a duplicate of this SO question because I am using require and refer, as the answer to that question suggested.
EDIT: the following also do not help
(add (records.core/my-vector. []) 42)
(add (#'records.core/my-vector. []) 42)
(add (##'records.core/my-vector. []) 42)
Unless my defrecord skills become rusty, you should first require the namespace and then import the defrecord.
(:require records.core)
(:import [records.core my-vector])