Fork me on GitHub
#beginners
<
2020-06-29
>
ryan echternacht03:06:51

I have a project setup with deps.edn . I have an alias for nrepl (from Sean Corfield’s guide). I can start the nrepl with clj -A:nrepl , how do I get it to auto-load my project like lein repl does?

practicalli-johnny09:07:32

If you are starting a REPL on the command line and include nREPL, then I assume you are going to connect to that REPL from an editor. So once the editor has connected, evaluate the source code file that defines your main namespace and that loads your project. Unless you need to start a REPL on the command line, starting the REPL from within the editor is recommended practice. This ensures the right version of dependencies are included for the editor to connect to the REPL that is started by the tool the editor launches to start the REPL (clojure cli, leiningen, etc)

practicalli-johnny10:07:26

If you definitely want to load in a namespace each time you start the REPL, then a common approach is to use a user.clj file with an (ns user) namespace, which will be loaded when starting a REPL. When using a deps.edn project, using a :dev alias is recommended so that a choice can be made whether to start the REPL with the additional code in the project user namespace. https://practicalli.github.io/clojure/clojure-tools/configure-repl-startup.html

seancorfield03:06:49

@ryan072 Do you mean the "main" ns that lein repl loads and starts you in?

seancorfield03:06:47

If you were starting a regular Clojure CLI REPL, you could use -e "(require 'my.main)" -e "(in-ns 'my.main)" -r and it would run those commands and then present a REPL.

seancorfield03:06:39

But the :nrepl alias uses :main-opts and they do not combine across aliases so I'm not sure if that will work...

ryan echternacht03:06:11

I guess what’s your recommendation for “loading” my project into the nrepl?

ryan echternacht03:06:23

just go execute the “main” manually file and have it load everything else?

seancorfield03:06:34

I never type into the REPL. When I evaluate forms from the editor, it auto-selects the correct ns for the evaluation.

ryan echternacht03:06:52

when you fire up your repl, is your project “loaded” already, or do you load it yourself?

ryan echternacht03:06:59

loaded/parsed/whatever the term is

seancorfield03:06:31

The "project" isn't loaded. Each namespace is loaded/compiled when it is required.

ryan echternacht03:06:07

and you do that yourself? (as in you personally evaluated the what you need to get started). unless i”m crazy, doesn’t lein repl already evaluated your entire project?

ryan echternacht03:06:26

not that I need it to be like lein repl just want to make sure I’m not setting stuff up incorrectly?

seancorfield03:06:53

I haven't used lein for years so I don't really remember how it works. I don't use nREPL either.

ryan echternacht03:06:33

what do you use?

seancorfield03:06:58

The Clojure CLI. And a plain Socket REPL.

seancorfield03:06:28

What editor do you use?

seancorfield03:06:33

OK, so you start an nREPL server, then you connect Calva to it...

seancorfield03:06:57

Then you evaluate the current file into the REPL? Load file?

ryan echternacht03:06:17

typically just eval a form

ryan echternacht03:06:28

and it switches ns and does it correctly

ryan echternacht03:06:38

just felt like the project should be ‘pre-evaled’

noisesmith15:06:09

This can be set up by adding a startup form that does so. The annoying thing about this option being on by default (as in lein) is when you have a single broken namespace it can leave you in an inconvenient space in terms of what clojure thinks is loaded already via require and which vars are in fact defined

seancorfield03:06:09

I wouldn't want the REPL to load stuff on its own.

seancorfield03:06:17

I would want control over what gets loaded.

seancorfield03:06:58

I work with a large project that has many main entry points -- there's no single namespace that could be required at startup to load "everything".

seancorfield03:06:32

I really hadn't given any thought to the idea of a namespace being auto-required at startup, since the editor always uses the right ns for evaluation. The only caveat is that you have to eval the ns form first in order to have all the other nses compiled that your namespace uses.

seancorfield03:06:49

Or you could just "load file" whatever that is for your editor.

seancorfield03:06:50

I have ctrl-; f bound to "load file" and ctrl-; B for "eval top-level form" (and ctrl-; b for "eval current form").

solf03:06:07

If I'm not mistaken, lein repl only evaluates whatever namespace you put in :main on the project.clj file. On most projects, that main namespace's requires will eventually load all/most of your code, unless it's a big project with multiple entry points.

seancorfield03:06:51

Yeah, that sounds likely. I suppose it saves a keystroke on small projects when you're first starting your REPL. My REPL tends to run for days, sometimes weeks, so it's not much of a saving.

ryan echternacht03:06:19

thanks for the responses @seancorfield!

ryan echternacht03:06:26

glad to know that I’m not missing something obvious too

seancorfield03:06:41

I guess it's another one of those ways that lein makes things "easy" (without being "simple") 🙂

seancorfield03:06:54

(after paying attention to how I work for a few minutes, apparently I've gotten into the habit of hitting ctrl-; f pretty much every time I open a file... and since my REPL doesn't have a prompt I don't even see what ns it is in...)

icosahedron05:06:28

Just joined. Enjoying Clojure so far. I’m working in an ML class, and thought Clojure would be a good way to explore some ML in the Java world. I didn’t find any Clojure specific ML libraries, so I ended up using SMILE which has some Clojure bindings. Did I miss anything during my Google searches? Are there more interesting libraries than SMILE for data analysis/ML in Clojure? I’m running clojupyter and it’s pretty good. Not as well integrated as Python of course, but overall it’s pretty handy, and Oz/Vega-Lite is producing decent graphs.

seancorfield05:06:46

You'll probably also find this guy's stuff interesting https://dragan.rocks/

icosahedron06:06:28

I have. It looks interesting, and I really like the GPU components, but I was looking for something similar to scikit-learn for Python, which Smile does a good job. Fastmath looks like it wraps smile and other libraries fairly well, so I’m going to look at that more in-depth. Thanks.

seancorfield05:06:07

There's a #data-science channel here as well (although I think a lot of the data science folks are more active on the Clojurians Zulip chat setup?)

👍 3
jumar05:06:33

I'd recommend asking in #data-science This is also a good resource: https://scicloj.github.io/pages/libraries/

icosahedron06:06:05

Oo. That page looks interesting. Fastmath in particular looks good, and it uses smile as its backend for a lot of items.

pez06:06:05

@ryan072 check out https://calva.io/try-first/ , if you haven't. Note that it starts out by the user loading the current namespace. It could seem silly you have to do this yourself, but as @seancorfield points out, you should be in control of what is being evaluated, when. (Calva could be a bit more helpful in reminding you about evaluating forms and files, but anyway.)

Mattias07:06:14

Question on Clojure installation om MacOS (unsure if there is a better channel). Installed with Homebrew a while back and now get warnings like: “Warning: Calling 'devel' blocks in formulae is deprecated!...”.

Mattias07:06:42

Is it my install or something that will be fixed elsewhere? Thanks 🙂

naxels15:06:41

I have the same issue, needs to be fixed in the tap

Mattias16:06:11

Thanks! 👍

nthd3gr3310:06:07

Not sure if this is the correct way to ask this question, but here it goes: Within the Java API is it possible to compile a Clojure program from a string?

Daniel Stephens10:06:05

Haven't done this myself but this might help: https://clojure.org/reference/java_interop#_calling_clojure_from_java This is a guess but you could try something like:

IFn eval = Clojure.var("clojure.core", "eval");
eval.invoke(Clojure.read("your string"));

Daniel Stephens10:06:27

Actually think you might be able to skip the IFn step and just use eval directly from the compiler clojure.lang.Compiler.eval(clojure.java.api.Clojure.read("(do (println \"hello world\" {:a :b}) 123M)"))

nthd3gr3314:06:47

We used your first suggestion, and it is working great. I really appreciate the help @ULNRSUK8C 🙏:skin-tone-3:

Daniel Stephens14:06:46

Glad it worked out 🙂 have fun!

MorongÖa12:06:56

I have converted a js object to clj using (js->clj obj). This is what I get back: {"foo" nil, "bar" #object[com$core$test]} . How do I get the value of foo and bar?

adam13:06:50

(x "bar")

Shreyanshi Bharadia13:06:33

Can someone suggest me a good Mark Down Editor which can be easily integrated with cljs? If possible can you give me some examples also about how to use it. A bit new to Clojure.

adam13:06:05

IntelliJ/Cursive is tightly integrated with markdown. If you type the following for example: >

clojure
> (contains? x)
> 
It will show you usage error highlighting and even allows you to jump to the function definition.

👍 3
Casey15:06:39

I want to create a spec that validates variable length vectors with different predicates based on their first element. These vectors are commands expressed as data What would be the best way to define these specs? (fake) example: • [:add 1 2] would have a spec that validates that values at index > 0 with int?[:print "Hello world"] would validate with string? (and perhaps assert only one value)

Alex Miller (Clojure team)15:06:48

use an s/multi-spec keyed off the first element of the vector

Alex Miller (Clojure team)15:06:15

I've done similar things a few times, works well

Alex Miller (Clojure team)15:06:45

b/c it's based on a multimethod, it's also open to later extension (which was the whole point behind s/multi-spec)

Casey15:06:58

hm nifty, I've only used multimethods with maps, not vectors, but I suppose a multimethod like

(defmulti command-type first)
would work here?

Casey16:06:03

Really cool, this works great. Thanks @alexmiller

Casey16:06:31

Is there a spec shorthand to match a literal keyword? (s/tuple #(= % :add) int? int? works, but is a little verbose?

Alex Miller (Clojure team)16:06:07

enumerated set as a function

Casey16:06:54

doh, of course, one of the first things you learn with spec. I guess I haven't matched literal values that often

noisesmith16:06:58

NB that works for all values but false, nil, and NaN

noisesmith16:06:37

false and nil because match returns falsey, NaN because it won't match

Mark-James M.16:06:22

What's the recommended Clojure stack for a CRUD web app? I'm looking to create a site which allows people to add location data to an API map, track the locations the user added, and allow other users to see the locations

noisesmith16:06:15

all else being equal, I recommend the stack used by "Web Development In Clojure" https://pragprog.com/titles/dswdcloj3/

👍 3
noisesmith16:06:29

because that way you can learn from the book, which is very good

noisesmith16:06:52

and unlike many books, it uses tools that are actually widespread and up to date in the community

Mark-James M.16:06:59

This looks perfect, The getting Clojure book from that series has been a huge help

Kazuki Yokoyama19:06:23

Hi guys, I'm trying to test some functions that are implementation of a protocol while stubbing other functions of the same protocol. The scenario is like the following:

(ns my.app.protocol.service)

(defprotocol Service
 (foo? [service args]) ;; Predicate function.
 (bar [service args]))

(ns my.app.impl.service)

(defrecord ServiceImpl [,,,]
  (foo? [service args] (return-true-or-false))

  (bar [service args]
    (if (foo? service args)
      (do-something-if-true)
      (do-something-if-false))))
How can I stub foo? while testing bar? I've tried reifying foo? and using with-redefs but didn't manage to make it work. Any ideas? Thank you.

dpsutton19:06:31

Clojure 1.10.1
(defprotocol Foo
  (pred [_])
  (action [_ options]))
Foo
(action
  (reify Foo
    (pred [_ ] true)
    (action [this options]
      (if (pred this) :i-said-yes :i-said-no)))
  {:dummy :options})
:i-said-yes
user=>

dpsutton19:06:23

i'm not seeing the problem. this seems to work

Kazuki Yokoyama20:06:05

It almost did the trick for me. But I still need to call the actual action , like this:

Foo
(action
  (reify Foo
    (pred [_ ] true)
    (action [this options]
      (actual-call-to-action options)
  {:dummy :options})
since it is the function I want to test.

seancorfield19:06:42

@yokoyama.km This is why it is common practice to wrap protocol functions so that you can stub the wrapper instead. You cannot instrument a protocol function either, so you have to use a wrapper function in order to leverage instrument.

seancorfield19:06:46

See, for example, https://github.com/seancorfield/next-jdbc/blob/develop/src/next/jdbc.clj where the API is all wrapper functions around calls to protocol functions -- a common pattern in Clojure.

Kazuki Yokoyama19:06:33

Yes, you've already mentioned that, I'm sorry I didn't connect the dots. I'll try it. Thank you, @seancorfield

Kazuki Yokoyama20:06:10

If you don't mind answering some questions: 1) For example, transact is the wrapper around the -transact function of the Transactable protocol, right? In tests I can then stub transact by passing a reified transactable (and I can even add instrumentation to transact). 2) The clients that currently use the protocol's function directly (by passing it any valid implementation of the protocol) would switch to use the wrapper instead, is it right?

seancorfield21:06:14

No client code should use the protocol directly. The API is the wrapper functions (not the protocol).

seancorfield21:06:48

You don't need to reify anything. You simply use with-redefs on the wrapper, just as you would call instrument on the wrapper.

seancorfield21:06:18

In other words, the protocol becomes just an implementation detail: the wrapper function is all you should care about.

seancorfield21:06:47

Any client that extends the protocol will still call it via the API (the wrapper).

Lennart Buit19:06:58

In this particular case, you can make a proxy of sorts. Say you have an x satisfying Service, you can create a function taking that x and reifying Service and forwards all calls to x but the ones you wish to stub. Not to undermine the advice Sean already gave you ^^

Kazuki Yokoyama20:06:17

You say something like this?

(let [x (map->Service {})
      stub (reify Service
             (foo? [service args] true)
             
             (bar [_ args] (protocol/bar x args)))])

Lennart Buit20:06:50

Yes, something along those lines yeah

Kazuki Yokoyama20:06:50

But when bar of x executes, it uses x's implementation of foo? not the one reified, right?

Lennart Buit20:06:45

Uh right, yeah, my bad

Kazuki Yokoyama20:06:12

Thank you anyway! 😄

rig0rmortis20:06:01

can I pass a conditional into a threaded macro? I'm trying to do this:

->> data
    (if (= "something" "something") 
        function-a
        function-b)
    print
I get the error Too many arguments to if

Kazuki Yokoyama20:06:58

Are you trying to pass data to function-a and function-b?

bhaim12320:06:08

I think macroexpand is your friend when you want to know what really happens in a macro:

user=> (macroexpand
macroexpand     macroexpand-1
user=> (macroexpand '(->> data
  #_=>     (if (= "something" "something")
  #_=>         function-a
  #_=>         function-b)
  #_=>     print))
(print (if (= "something" "something") function-a function-b data))

rig0rmortis20:06:45

ah I'm trying to pass data to function-a or function-b, based on the conditional

rig0rmortis20:06:57

good tip, thanks @UU9PM3M88 lemme poke around, yeah the result I want here is:

(print (if (= "something" "something") (function-a data) (function-b data))

Kazuki Yokoyama20:06:47

Take a look at cond-> , maybe it can help

rig0rmortis20:06:56

thank you, will do

Kazuki Yokoyama20:06:36

But you can enclose your original if form in another pair of parentheses and it will do the job (though it will be confusing):

(->>
  {:a 1 :c 2}
  ((if true
    (fn [d] (:a d))
    (fn [d] (:b d))))) ;;=> 1

rig0rmortis20:06:36

ended up with this:

->> data
    (#(if (= "something" "something") 
        (function-a %)
        (function-b %))
    print
which seems to work, though I'm not sure if this is bad practice or not 😛

rig0rmortis21:06:08

this seems cleaner actually:

->> data
    transform
    print
and just put the conditional inside transform
(defn transform [x]
  (if (= "something" "something")
      (function-a x)
      (function-b x))) 

sova-soars-the-sora21:06:29

Much nicer! Well done. Yes, you can put ifs inline like you did but the mantra is "don't make me think"

🙂 3
Rabie21:06:32

Hello, I have a question about how to cleanly structure code in clojure coming from a Object Oriented background. Let's say I have two function f1 and f2. > f1 uses f1_auxA and f1_auxB. f1 is the only one to use them > f2 uses f2_auxA and f2_auxB. As for f1, f2 is the only one that uses f2_auxA and f2_auxB From what I understand I have to define all these functions in the same file. That's okay for me as long as there are not that many functions. However is there a way to structure the code so I can easilly know that f1_auxA and f2_auxB are only related to f1 and the same thing for f2? I understand I can put them in different files/namespaces, but it might lead to a lot of files for a big project. In OO I would just group related functions in the same class. Are other good practices that you use for this kind of purpose? Thank you for your feedbacks Rabie

noisesmith21:06:16

there is no requirement of having things in the same file

noisesmith21:06:08

you can use letfn to make them only available in the parent function - (this is literally an inner class if you have heard of those, on the implementation end)

noisesmith21:06:34

I would think of a namespace as equivalent to a package (this is how they compile, when you don't use the :gen-class directive in the ns form)

noisesmith21:06:28

the normal way of doing things in a lisp is to group functions by domain, and define them to use standard off the shelf data structures (like lists, vectors, sets, hash-maps)

noisesmith21:06:22

also, the conventional wisdom is that privacy / data hiding is much less needed when all your values are immutable

noisesmith21:06:08

our VM is OO, and implementation wise, literally speaking: • functions are classes implementing clojure.lang.IFn which is "callable" via its .invoke method. This is pretty much never done as the clojure compiler creates that invocation for you. • our data structures are classes implementing java.lang.Collection with various interfaces providing extra features (java.util.Map, java.util.List, etc.) • we don't use concrete inheritance unless forced to by someone else's API, we use interfaces to define code that can extend existing functions by implementing the methods they use

Rabie21:06:28

Oh great thanks for the detailed answer. I gues letfn is the answer to what I need because I need structuring but not to the extent of a package. If I understand correctly letfn it is a sort of local function the same way we define local variable with let. Right?

noisesmith21:06:29

it defines 0 or more local functions that can mutually call one another

noisesmith21:06:49

(this is important because normally in clojure bindings can't forward-reference things you have not defined yet)

noisesmith21:06:26

there's also defn- for functions that should have ns scope but are not meant to be used (and can't be used conventionally) outside the ns where they are defined

Rabie21:06:56

Thank you very much for these explanations

andy.fingerhut23:06:17

If you do not need earlier local functions to call later ones, you can also simply use let

🙏 3
sova-soars-the-sora21:06:45

Is there a channel to discuss clojure videos?

andy.fingerhut23:06:14

I am not aware of a channel specifically for discussing videos. I think it is reasonable to ask questions about the contents of Clojure videos in an otherwise appropriate channel for the topic.

andy.fingerhut23:06:23

e.g. if the Clojure video you want to discuss is about Clojure the language, and not-terribly-beginner-focused, then #clojure. If beginner-friendly, #beginners. If focused on some IDE or similar dev environment, then most of those have dedicated channels, e.g. #emacs #cursive #calva #chlorine

💯 3
🎵 3
robertfw21:06:13

I've been looking without much luck for discussion about the suggestion in the docstring for empty?:

dev=> (source empty?)
(defn empty?
  "Returns true if coll has no items - same as (not (seq coll)).
  Please use the idiom (seq x) rather than (not (empty? x))"
  {:added "1.0"
   :static true}
  [coll] (not (seq coll)))
I understand why (seq x) works, I've just found that it's easier to read (and thus easier for me to continue pushing adoption at work) to use some form of (empty? x) . I see that using empty? results in an additional call to not , but are there any other reasons to prefer this idiom?

noisesmith21:06:40

the idiom of seq returning a falsey result for an empty input is considered core to the language

noisesmith21:06:06

it enables direct translation of common-lisp code where an empty list compares false

noisesmith21:06:53

so to some degree it could be called "historical reasons" from lisp history, but regardless, it's a lot easier to understand clojure if you internalize seq returning falsey for empty input

phronmophobic21:06:14

basically, what noisesmith said. seqs are core to clojure and conveying idiomatic usage and behavior of core parts of the language is useful

robertfw21:06:50

Sure, I am on board with that in principle. I'm choosing some battles in terms of style in order to make code approachable and this is one I'm willing to cede on, but wanted to make sure there wasn't some other reason behind it

👍 3
phronmophobic21:06:55

imo, if using (not (empty? x)) helps you to sneak clojure in the door, then I'm all for it

noisesmith21:06:51

@robertfrederickwarner nb. you can use not-empty to get falsey on empty like seq gives, but not changing the data type if non-empty

noisesmith22:06:48

(ins)user=> (seq "hi")
(\h \i)
(ins)user=> (defn foo [c] (or (not-empty c) :default))
#'user/foo
(ins)user=> (foo "hi")
"hi"
(cmd)user=> (foo "")
:default

robertfw22:06:44

Thanks, I'll have a peek at our uses and see if any can benefit from that