beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
kozmicluis 2020-11-09T01:40:12.385700Z

Is there a way to provide a docstring to a clojure function defined in terms of def instead of defn ?

kozmicluis 2020-11-09T01:40:39.386300Z

I have some functions that could benefit from the point-free style but I don't see a way to add metadata or a docstring

2020-11-09T01:43:18.386800Z

def takes an optional doc string argument, and metadata can be provided on the symbol itself

2020-11-09T01:44:35.387100Z

(ins)user=> (def ^{:foo [1 2 3]} f "takes no args and returns nil" (fn f []))
#'user/f
(ins)user=> (doc f)
-------------------------
user/f
  takes no args and returns nil
nil
(ins)user=> (meta #'f)
{:foo [1 2 3], :line 1, :column 1, :file "NO_SOURCE_PATH", :doc "takes no args and returns nil", :name f, :ns #object[clojure.lang.Namespace 0x6f012914 "user"]}
user=> 

kozmicluis 2020-11-09T01:46:21.388100Z

Ahh I see

ghadi 2020-11-09T01:46:22.388300Z

@luishendrix92 you can type (doc def) and see more

kozmicluis 2020-11-09T01:46:23.388400Z

thanks

Jim Newton 2020-11-09T09:02:41.393200Z

How can I load code created by another JVM language? Does it suffice to ask the other language to produce a .jar file? Can I load the .jar file explicitly in clojure? Or is it more complicated than that? E.g., does it have to be installed into some mutually agreed directory with a bunch of boiler plating surrounding it? I'd prefer to have something light which I can later just delete with no lasting side effects.

2020-11-09T09:12:40.393400Z

clojure usually (unless you encode how and what to compile using system tools) have no idea how to compile another PL so to use libs written for JVM but in another language (eg. scala) it should be provided as compiled jar there are some options to load jar file, all depends on how you run your application: 1. if you run using java command — add this jar to classpath (`-cp` option) 2. if you running application using clojure cli (aka. tools.deps) — you can add dependency pointing to that jar (`{:deps {some/id {:local/root "/path/to/file.jar"}}}`). in that case jar file can be placed anywhere 3. you can “install” that jar into local maven repository (usually $HOME/.m2/repository/…)

Daniel Östling 2020-11-09T09:32:30.395700Z

I’m sure this is answered already; when I add more require statements in for example core.clj, and in project.clj, what steps do I have to take in Emacs/Cider environment to get the editor pick that up? cider-restart doesn’t seem to be enough, I have to shut down the whole cider/nrepl process it seems.

Daniel Östling 2020-11-09T09:32:54.395800Z

Oh, I’ve done lein deps to download the deps as well.

Jim Newton 2020-11-09T09:34:57.396Z

Currently i'm running clojure in two different ways. from the lein command line either via "lein test" or "lein repl", and also running from cider within emacs. At the moment, I'm not interested in installing applications based on .jars coming from different JVM langauges, rather I'm just interested in using the clojure reflection (`clojure.reflect/type-reflect`) capabilities to examine classes which were created elsewhere. elsewhere being directly from java or from Scala.

2020-11-09T09:35:45.396200Z

running lein repl or start repl from emacs should be enough but will require restart of the repl in case you add dependencies in project.clj

Jim Newton 2020-11-09T09:37:12.396400Z

So in these cases would it suffice just to persuade java or Scala to create a single .jar file? For example. I'd like to use clojure.reflect to examine the classes produced by the following scala code.

trait A {
  def foo(s: String): AnyRef
}

trait B {
  def foo(s: String): String
}

class Foo extends A with B {
  override def foo(s: String): String = s
}

Daniel Östling 2020-11-09T09:37:57.396700Z

Yeah okay. Thanks 🙂

2020-11-09T09:39:50.396900Z

I can’t say much about scala workflow, specifically about the content of produced jar file. but it should be sufficient if at the end this jar file will have a bunch of .class files which can be loaded in clojure for analysis

Jim Newton 2020-11-09T09:41:26.397100Z

so is the .jar a sort of tar file of .class files? Can I put the .jar in /tmp/erase-me-tomorrow/file.jar and then somehow tell lein to load it so that the classes are available at the clojure repl??

Jim Newton 2020-11-09T09:42:33.397400Z

or is it the case that the .jar file might actually be any of many many different things depending on the language which produced it?

2020-11-09T09:47:45.397600Z

JAR (or JavaARchive) is just a package file format. there are no restrictions to file types it contains. Typically it contains many java .class files optionally together with source file used to produce those .class’es so in general .jar file might contain anything depending on PL

2020-11-09T09:49:10.397800Z

for instance — I saw jar files produced by javascript with resources meant to be used in java application

2020-11-09T09:49:52.398Z

https://cljsjs.github.io/ like here

2020-11-09T09:50:47.398300Z

jdk also providing a tool to examine content of jar files jar tf /path/to/file.jar

2020-11-09T09:54:42.398500Z

(answering on the question about leiningen) if I’m not mistaken — to add local jar via leiningen you should use :resource-paths ["/path/to/local.jar" …]

2020-11-09T09:55:05.398700Z

path can be absolute or relative to project’s root

Jana Chaloupková 2020-11-09T10:20:23.404300Z

Hello! :3 Would someone be willing to help me out with an exercise I'm doing? It's a transcription of DNA to RNA and this is what I came up with:

(ns rna-transcription)

(defn- valid? [dna]
  (nil? (re-find  #"[^CGTA]" dna)))

(defn to-rna [dna]
  (let [modify-letters (map {\G "C" \C "G" \T "A" \A "U"} dna)]
    (assert (valid? dna))
    (apply str modify-letters)))
However, I was asked for further edits/optimalisation to avoid nil? (done), to do validation based on count (done) and (what I'm struggling with) to exchange map for keep (filters nil automatically and I don't iterate over the string again).
(ns rna-transcription)

(defn- valid? [dna, rna]
  (= (count dna) (count rna)) true)

(defn to-rna [dna]
  (let [rna (keep #{\G "C", \C "G", \T "A", \A "U"} dna)]
    (assert (valid? dna rna))))
I don't understand how to use keep instead of mapping for transcription though. :/ Thank you in advance!

2020-11-09T10:25:46.404700Z

just change #{\G "C", \C "G", \T "A", \A "U"} to {\G \C, \C \G, \T \A, \A \U} a map is a function of key in clojure but #{ is a set literal - https://www.clojure.org/guides/learn/syntax#_literal_collections

Jana Chaloupková 2020-11-09T10:28:42.404900Z

Thank you! I was getting a bit desperate and trying out everything. 😅 This still fails tests though.

lein test :only rna-transcription-test/it-transcribes-all-nucleotides

FAIL in (it-transcribes-all-nucleotides) (rna_transcription_test.clj:18)
expected: (= "UGCACCAGAAUU" (rna-transcription/to-rna "ACGTGGTCTTAA"))
  actual: (not (= "UGCACCAGAAUU" nil))

lein test :only rna-transcription-test/transcribes-cytosine-to-guanine

FAIL in (transcribes-cytosine-to-guanine) (rna_transcription_test.clj:6)
expected: (= "G" (rna-transcription/to-rna "C"))
  actual: (not (= "G" nil))

2020-11-09T10:30:51.405100Z

right, you have (assert … expression inside of let this is not really clear from the docstring but assert returns nil in case underlying expression resulting to trythy value

2020-11-09T10:32:01.405300Z

(defn to-rna [dna]
  (let [rna (keep {\G \C, \C \G, \T \A, \A \U} dna)]
    (or (assert (valid? dna rna)) rna)))

2020-11-09T10:32:46.405500Z

there is one of a few “valid” changes to do what you want

2020-11-09T10:33:56.405700Z

or you can be a little bit more explicit and use if here

(if (valid? dna rna) rna (throw (ex-info "Invalid DNA sequence" {}))

❤️ 1
Jana Chaloupková 2020-11-09T10:39:15.406Z

Works like a charm, thank you!

oly 2020-11-09T11:40:36.407600Z

anyone know if cider has a way to specify a fn to run on jack in ? I see a lot of people creating startup shutdown reload fn's so I am guessing no but it would be nice to jack in and have the startup fn executed instead of typing it each time

practicalli-john 2020-11-09T11:57:13.407800Z

keep is a nice approach, thanks. I just used a map with an anonymous function to wrap the or around the hash-map, which is used as a function over each character in the dna string. If the character is not found, then throw the error.

(defn to-rna
  [dna]
  (string/join
    (map #(or (dna-nucleotide->rna-nucleotide %)
              (throw (AssertionError. "Unknown nucleotide")))
         dna )))
with the hash-map to define the transformation as a simple dictionary
(def dna-nucleotide->rna-nucleotide
  "DNA to RNA transcription mappings"
  {\G \C
   \C \G
   \T \A
   \A \U})

❤️ 1
Jim Newton 2020-11-09T12:01:56.408900Z

is my understanding correct that I cannot use {:pre ... :post ....} within a defmethod?

practicalli-john 2020-11-09T12:01:57.409Z

@oliver.marks functions can be called when starting a REPL (regardless of how they are started) https://practicalli.github.io/clojure/clojure-tools/projects/configure-repl-startup.html

oly 2020-11-09T12:04:17.410500Z

oh that makes sense just call it in the file, so if i create user.clj and make that the namespace and call the startup there at the top level, I was over complicating it in my head 🙂

practicalli-john 2020-11-09T12:05:21.411300Z

@oliver.marks there are cider specific variables for restarting the repl via cider which builds upon that approach https://practicalli.github.io/spacemacs/clojure-repl/component-lifecycle.html

oly 2020-11-09T12:06:09.411600Z

awesome thanks for the links @jr0cket

oly 2020-11-09T12:07:32.412500Z

I was aware of integrant never realised it had hooks into cider that will be super handy and gives me a reason to use that library now 🙂

Dave Russell 2020-11-09T12:15:32.416400Z

Is there a way to give a set of keywords the same spec, without having to specify each one like this:

(s/def ::foo fancy-spec)
(s/def ::bar fancy-spec)
(s/def ::baz fancy-spec)

2020-11-09T12:19:10.417500Z

I'm working through The Little Schemer in Clojure and have arrived at the last chapter. It uses data types from Scheme for the examples and I'm not sure how to approach it from Clojure. Anyone happen to have done it before? They define two functions that rely on Scheme's *const, *quote, *identifier, *lambda, *cond, and *application and then build upon that in later problems:

(define atom-to-action
 (lambda (e)
  (cond
   ((number? e) *const)
   ((eq? e #t) *const)
   ((eq? e #f)  *const)
   ((eq? e (quote cons)) *const)
   ((eq? e (quote car)) *const)
   ((eq? e (quote cdr)) *const)
   ((eq? e (quote null?)) *const)
   ((eq? e (quote eq?)) *const)
   ((eq? e (quote atom?)) *const)
   ((eq? e (quote zero?)) *const)
   ((eq? e (quote add1)) *const)
   ((eq?   e (quote sub1)) *const)
   ((eq? e (quote number?)) *const)
   (else *identifier))))

(define list-to-action
 (lambda (e)
  (cond
   ((atom? (car e))
    (cond
     ((eq? (car e) (quote quote))
      *quote)
     ((eq?   (car e) (quote lambda})
       *lambda)
     ((eq? (car e) (quote cond))
      *cond)
     (else *application)))
   (else *application))))
https://pdfs.semanticscholar.org/35d0/d5275a8390c351ce98fbdc2ad37d210ba63b.pdf page 181

practicalli-john 2020-11-09T12:24:38.417600Z

I havent had chance to create an integrant example, but it should work just like mount (with slightly different libraries)

alexmiller 2020-11-09T12:57:58.418800Z

You can s/def one keyword to another

2020-11-09T13:02:00.419Z

You can, place it after arglist definition (defmethod foo :dispatch-value [& args] {:pre pre-fn :post post-fn} …)

2020-11-09T13:03:56.419200Z

user=> (defmulti adder :k)
#'user/adder
user=> (defmethod adder :y [{:keys [y]}] {:post [(pos? %)]} (inc y))
#object[clojure.lang.MultiFn 0x5286c33a "clojure.lang.MultiFn@5286c33a"]
user=> (adder {:k :y :y 1})
2
user=> (adder {:k :y :y -2})
Execution error (AssertionError) at user/eval140$fn (REPL:1).
Assert failed: (pos? %)

1
Jim Newton 2020-11-09T14:07:20.419800Z

is that what fn-tail means in the https://clojuredocs.org/clojure.core/defmethod?

2020-11-09T14:17:40.420Z

yes, more precisely — https://github.com/clojure/clojure/blob/clojure-1.10.1/src/clj/clojure/core.clj#L1787 have a look at how defmethod is implemented (fn & fn-tail) so fn-tail can have everything that can take fn like a function name to produce better error stack traces or extra metadata etc.

yubrshen 2020-11-09T16:53:09.421600Z

How can I create a project with Clojure (Clj) with some template mechanism to have the directory structure like that produced by lein new app but using deps.edn? I tried the following:

clojure -M -m myname.sandbox
with @seancorfield’s clj-new aliases, but the directory structure is sandbox/src/myname/sandbox.clj, but I need it to be sandbox/src/sandbox/core.clj I wonder if I could just use lein new app sandbox and add deps.edn by manually translating project.clj to deps.edn, I wonder if it would work to use clj for execution and continuous tests. I tried the following. It did run with project.clj:
clojure -M -m sandbox.core

alexmiller 2020-11-09T17:03:47.422900Z

I doubt I can answer your question, but I'm missing what "doesn't work" means - error? different structure than expected? if so, what structure?

Dave Russell 2020-11-09T17:06:02.423Z

Thanks! Is it possible to do it in bulk? Say I have 20 keywords that I all want s/def'd to the same spec, and don't want to repeat myself for 20 lines 🙂

alexmiller 2020-11-09T17:08:11.423200Z

not part of spec, but macros exist :)

Dave Russell 2020-11-09T17:10:16.423400Z

Understood -- thanks!

yubrshen 2020-11-09T17:14:53.424600Z

@alexmiller yes, it's different structure. I've improved my question. Thanks!

2020-11-09T17:20:29.425300Z

you may want to checkout the last paragraph in https://github.com/seancorfield/clj-new#getting-started

yubrshen 2020-11-09T17:39:35.427200Z

@hiredman Thanks for the pointer. With minor variation, the following achieves the structure that I want:

clj -X:new-app :name sandbox.core

seancorfield 2020-11-09T17:55:06.428500Z

@yubrshen foo.core is a bit of an anti-pattern -- the only reason .core is prevalent in Clojure is because Leiningen added it to single-segment names as a default to avoid ending up with single-segment namespaces (and therefore top-level class names in JVM byte code).

👍 1
seancorfield 2020-11-09T17:57:00.430400Z

That's why clj-new strongly encourages you to pick a proper name for your project -- either <orgname>/<myproject> or <username>/<myproject> -- and the Clojure CLI also discourages libraries with the same group and artifact ID (i.e., use a proper orgname or username in both your project and your top-level namespace segment).

🙏 1
seancorfield 2020-11-09T17:57:52.431200Z

Even when building an application, following that "proper" naming pattern makes it less likely your own namespaces will conflict with any third party library code you use.

seancorfield 2020-11-09T17:59:28.432700Z

(the author of Leiningen is on record as regretting his decision to add .core by default and says he should have required multi-segment names -- and lein new will already reject a bunch of names so it's not like it isn't already opinionated 🙂 )

practicalli-john 2020-11-09T19:37:11.440100Z

@yubrshen Unless a Leiningen project relies on certain plugins that inject code or config, then it should be just a matter of manually adding the right :paths and :deps details are in the deps.edn file to run a project Leiningen project with Clojure CLI tools instead. I do this for http://Exercism.io and can just use {:paths ["src"]} as the only dependency is clojure and that is pulled in from the install configuration. You can always refactor the namepaces and file names once you have an understanding of the tutorial you are following.

Audrius 2020-11-09T20:07:02.440900Z

Hi, how to get :as all in the second argument?

(fn [db [_ new-filter-kw]]
    (assoc db :showing new-filter-kw))

2020-11-09T20:08:40.441Z

Do you mean you want a parameter named all to be equal to the value of the entire 2nd argument, which will be a sequential data structure of some kind (e.g. list, vector, etc.) ?

2020-11-09T20:10:02.441200Z

If so, here is a sample REPL session showing one way:

user=&gt; (defn foo [db [_ new-filter-kw :as x]] [db new-filter-kw x])
#'user/foo
user=&gt; (foo 1 [2 3])
[1 3 [2 3]]

Audrius 2020-11-09T20:12:14.441400Z

thanks, for some reason I was putting :as all outside of the argument array.

2020-11-09T21:16:01.446500Z

Hi everyone! Not sure if I should post this here or on the clojurescript channel but here it goes: I'm starting learning clojurescript and I'm setting up my development environment. My dev environment is a remote box running Spacemacs, so my idea is to run the clojurescript repl in that box connected to a browser in another machine. I've tried the default "browser" repl without success (even changing the "host" to my IP instead of "localhost") I've also tried figwheel (using the "flappybird" example) but the browser tries to connect to a websocket on "localhost" again. So my question is: how do I change the host on figwheel to my host instead of always pointing to localhost? Thanks!

2020-11-10T09:28:02.447500Z

Thank you for the reply. I tried that but didn't work 😞 I tried :connect-url, :server-ip, :ring-server-options {:host} But none of those seem to do anything. I'm addind those to the project.clj file

2020-11-10T11:47:45.449100Z

I'll post it in the right channel.