Fork me on GitHub
#beginners
<
2019-01-30
>
ben73502:01:26

What do Clojure folks call a single source file? For example in Python it's called a module. Presumably not a namespace, since I gather a namespace can be multiple files.

unni02:01:32

It would be a namespace πŸ™‚

seancorfield02:01:51

Mostly folks refer to a namespace since it's normal practice to have one namespace per file and one file per namespace.

seancorfield02:01:50

There are occasionally reasons to split a namespace over multiple files (as clojure.core does), e.g., for conditional loading of different code on different JVM or Clojure versions, but it is rare.

ben73502:01:08

Thanks @ and @seancorfield, that's good to know

seancorfield02:01:23

It's also worth bearing in mind that Clojure's "unit of compilation" is a top-level form (s-expression), not a file.

seancorfield02:01:17

And the compiler behaves very much like the REPL, reading and compiling (and executing) each form in a file sequentially as it is read in.

ben73502:01:19

Right, that explains why I totally screw myself up when I duplicate a file but forget to update the ns declaration.

andy.fingerhut03:01:33

Clojure require forms, and :require clauses inside an ns form, if the namespace is not already loaded, look for them in your Java classpath at a file name computed from the namespace name. The Eastwood lint tool does a check very early on and aborts with pretty clear warning messages if it finds any file name <-> namespace name mismatches. https://github.com/jonase/eastwood

ben73504:01:09

thanks @, that sounds very useful

ben73505:01:36

@ do you happen to know of an eastwood equivalent for ClojureScript?

andy.fingerhut18:01:54

I do not know of one. You could try asking on #clojurescript channel to see if anyone there does.

alexmiller02:01:53

I usually call it a source file

quieterkali05:01:59

{:added "1.0", :special-form true,
   :forms '[(fn name? [params* ] exprs*) (fn name? ([params* ] exprs*)+)]}
  [& sigs]
    (let [name (if (symbol? (first sigs)) (first sigs) nil)  ===> binding function name
          sigs (if name (next sigs) sigs)   ===> binding fn params and body
          sigs (if (vector? (first sigs))      ===> check if params is vector
                 (list sigs)                            ===> binding body to sigs
                 (if (seq? (first sigs)) ===>(need an example which make this cond be true)????
                   sigs                       ===> binding sigs to sigs
                   ;; Assume single arity syntax
                   (throw (IllegalArgumentException. 
                            (if (seq sigs)
                              (str "Parameter declaration " 
                                   (first sigs)
                                   " should be a vector")
                              (str "Parameter declaration missing"))))))

quieterkali05:01:02

this code is a part of defmacro fn can anyone help please?☝️

seancorfield05:01:33

Not sure what you're asking @quieterkali...?

seancorfield05:01:36

This is for (fn name [args] body) or (fn name ([args] body) ([args] body)) etc.

quieterkali05:01:14

i wanted to know what is passed to the fn macro a be able to follow the execution path. so just the fact to know the argument being passed help a lot πŸ˜„

quieterkali05:01:16

your explanation do helped a lot too. I am reading it carefully to make sure i got everything

seancorfield05:01:57

(where name is optional)

seancorfield05:01:07

So the first sigs in the let will be either ([args] body) or (([args] body) ([args] body))

seancorfield05:01:29

so you have (if (seq? '[args]) ...) or (if (seq? '([args] body)) ...) -- does that help?

quieterkali06:01:36

@seancorfield based on the kind of input fn take, i haven't been able to see a case where this state will be hit (if (seq? '[args]) ...) but in the first ifyes. am i missing something?

quieterkali06:01:29

this (fn name [args] body)`in the first if and (fn name ([args] body) ([args] body)) etc.` goes in the second if is it correct?

quieterkali06:01:51

@seancorfield Can you also please take a look to this code and tell me why psig (fn* [sig]? sig = sigs? if yes, so how comes?

[& sigs]
    (let [name (if (symbol? (first sigs)) (first sigs) nil)
          sigs (if name (next sigs) sigs)
          sigs (if (vector? (first sigs)) `([args] body)` or `(([args] body) ([args] body))`
                 (list sigs) 
                 (if (seq? (first sigs))    `(if (seq? '[args]) ...)` or `(if (seq? '([args] body)) ...)`
                   sigs
                   ;; Assume single arity syntax
                   (throw (IllegalArgumentException. 
                            (if (seq sigs)
                              (str "Parameter declaration " 
                                   (first sigs)
                                   " should be a vector")
                              (str "Parameter declaration missing"))))))
          psig (fn* [sig]
                 ;; Ensure correct type before destructuring sig
                 (when (not (seq? sig))
                   (throw (IllegalArgumentException.
                            (str "Invalid signature " sig
                                 " should be a list"))))

seancorfield07:01:06

I'm sorry, I don't understand your question.

quieterkali07:01:30

oh ok, let me try to be more clear

quieterkali07:01:26

please forget about my last question. i just saw the answer. i was being really stupid. thanks for your help πŸ™‚

quieterkali07:01:43

There is a map operating with sigs, which is being passed to fn* [sig] right after the first IllegalArgumentException. in the implementation

seancorfield05:01:58

(so it's an error if the first element isn't a vector or a seq)

seancorfield05:01:35

(since that first if is actually handled by the (if (vector? (first sigs)) ...) clause above)

veix.q512:01:30

how do i namespace a map? {:foo "bar"} is supposed to turn into #:hello{:foo "bar"}

manutter5112:01:40

Put the namespace on the keys β€” it displays the namespace outside the curly braces just to make it more readable.

zhouyiwan_clojurians12:01:19

How do I import java class from a java file? I see lein has ":java-source-paths" option, but how could I do this with deps & cli

seancorfield18:01:18

deps/cli is for running Clojure code. You'd need to compile the Java separately and then add the (compiled) .class files to your classpath when running the Clojure code that depends on it.

seancorfield18:01:06

lein (& boot) are "build tools" and know how to do a lot more than just run Clojure code. There are plugins for both that let you use deps.edn for your dependencies.

zhouyiwan_clojurians14:02:02

thx I did the way as you mentioned previously cause I prefer using deps & cli without lein and boot πŸ™‚

veix.q512:01:21

@manutter51 am i supposed to do this by casting keywords to string, join and then recast to keyword?

manutter5112:01:00

Here’s one way you could do it:

(def m {:foo 1 :bar 2 :baz 3})
=> #'user/m
m
=> {:foo 1, :bar 2, :baz 3}
(defn add-ns [m new-ns]
  (reduce-kv (fn [m k v]
               (let [new-k (keyword new-ns (name k))]
                 (assoc m new-k v)))
             {}
             m))
=> #'user/add-ns
(add-ns m "hello")
=> #:hello{:foo 1, :bar 2, :baz 3}

sb13:01:56

Hello, is there something similar like Linuxkit/Moby (Go) in Clojure/Java world? I didn’t find with Google. Or Buildroot/Yocto? (embedded system development)

sb13:01:13

Maybe https://github.com/nakkaya/ferret is similar a little bit.

nicholas.jaunsen13:01:36

I have a question about macros. I'm looking at the source code for clojures ẁhen macro.

(defmacro when
  "Evaluates test. If logical true, evaluates body in an implicit do."
  {:added "1.0"}
  [test & body]
  (list 'if test (cons 'do body)))

nicholas.jaunsen13:01:59

Couldn't this just have been implemented as a function?

dpsutton14:01:19

Functions eagerly evaluate their arguments. It would be evaluated before the test was run and would return the result of the body if the test were true or nil in the alternate case

nicholas.jaunsen14:01:08

ah, right. Thanks

dpsutton14:01:31

Clojure is not lazy despite what people might say. If the language were lazy --like Haskell--you could write this as a function

nicholas.jaunsen14:01:27

so is the reason for the cons here so that it returns a lazy-seq?

dpsutton14:01:34

no cons does not return a lazy sequence

nicholas.jaunsen14:01:02

oh I thought it was only constructing one list, but actually it is a list within a list (if test (do body))

leandrotk10017:01:29

It would be awesome to receive code review for this Luhn algorithm implementation I did

leandrotk10018:01:35

Feel free to add comments in the PR if you want it

mitchell_clojure18:01:54

Does that pass the test suite?

mitchell_clojure18:01:35

I'm very green on Clojure, but I think that instead of reversing the list of ints, you could load them into a vector and use peek and pop for more efficiency. Someone will have to correct me if I'm wrong.

mitchell_clojure18:01:40

On the "double-two-by-two" function

chase-lambert19:01:39

is there a way to have all my clojure repls or namespaces or whatever have the doc feature included? I find myself using it all the time and haven't figured out when I have access to it or not.

chase-lambert19:01:01

I basically want to be able to look up functions like (doc inc) whenever needed.

noisesmith19:01:54

in a new ns (after your initial one) you can (use 'clojure.repl) to access doc, etc.

noisesmith19:01:28

there might be a trick to making sure clojure.repl is always in repl scope...

chase-lambert19:01:29

that's my hope. It's not a huge deal but just curious. Like if I create a new app in leiningen and start the repl, clojure.repl seems to work right in that namespace. but other times, I'll start a different repl, be in the user namespace and still not have the functionality

dpsutton19:01:57

if you use CIDER, there is a menu accessed by hitting , (comma) in the repl that has require-repl-utils that i use often

dpsutton19:01:33

i wished the doc's and source macros worked with functions that are defined in your own namespaces. it doesn't seem to work like that

noisesmith19:01:04

doc does, source only works if you load your code from a file (which CIDER doesn't do for typical workflows)

chase-lambert19:01:37

why wouldn't core doc definitions just be included by default on all namespaces? does it use any resources?

noisesmith19:01:48

but you can use load-file or (require 'some.ns :reload) in the repl, after that source works on things from that file

noisesmith19:01:31

@chase-lambert the docs themselves are attached to the vars they document, it's a design decision that clojure.repl isn't automatically visible in every new namespace

chase-lambert19:01:14

cool. i'll work around it. no biggy. for some reason looking up docs as I learn doesn't take me out of flow. but seeing a unable to resolve symbol error instead makes my eye twitch

hiredman19:01:33

just use the full name of the function

hiredman19:01:39

(clojure.repl/doc ...)

hiredman19:01:43

that will even work in namespaces that don't have clojure.core referred

hiredman19:01:38

or stop switching namespaces, there really is very little reason that your repl's namespace should ever not be user

hiredman19:01:09

or open multiple repls in to the same process and keep one of them in user

chase-lambert19:01:50

that's true. i'm realizing i switched a lot of my repls to use rebel-readline and it doesn't have the functionality included in it's user ns on startup.

noisesmith19:01:23

that sounds like a good ticket for the rebel-readline project

chase-lambert19:01:26

but sometimes when I start a lein repl it starts me in user or it starts me in something like my main file like clojure-noob.core ns. I haven't figured out when it does which

chase-lambert19:01:00

ultimately i think i try out too many fun looking tutorials and examples I find online and it gets me using too many repls or namespaces beyond my current understanding.

seancorfield19:01:45

Your editor probably has a hot key to shows docs for the current symbol under the cursor @chase-lambert

seancorfield19:01:28

(a recommended workflow is not to type into your repl but to stay in your code editor and type code there and evaluate it into the REPL with whatever hot key your editor uses)

seancorfield19:01:05

Have you watched Stu Halloway's talks on "REPL-Driven Development" and "Running With Scissors"?

chase-lambert19:01:59

I think I did but when I was really fresh and just consuming all things clojure. Maybe now that I have a little time under my belt I can rewatch. I have tried to move my work flow to that but when I'm really digging deep into experimenting on a function I am still more productive just staying in the repl until I figure out what I'm doing.

seancorfield20:01:25

My workflow has developed around using (comment ..) forms in my code that contain what I would previously have typed into the REPL directly. That way you get full editor assist in writing your Clojure code, a history of your would-be REPL session, and it's easier to copy'n'paste your "scratch" code into actual functions and tests that you plan to keep.

seancorfield20:01:13

Stu mentioned this exact same workflow in one of his talks and called such things Rich Comment Forms -- partly because it's what Rich does too πŸ™‚

audiolabs20:01:26

Do you know what talk this was from?

seancorfield20:01:40

I think it was Running With Scissors... let me get the link...

seancorfield20:01:16

(Running With Scissors doesn't have a transcript yet @? πŸ™‚ )

audiolabs20:01:57

excellent, thanks

andy.fingerhut20:01:11

@seancorfield A very reasonable observation. I will consider creating one, but no promises on when. Will add an issue to that Github repo that may help remind me.

chase-lambert20:01:21

interesting! I have been seeing that (comment ...) thing in some clojure files I've been seeing lately.

seancorfield20:01:29

In particular, at work, we tend to have a (comment ..) form at the bottom of each file that contains various requires and setup code (such as creating and starting Components) that make it easier to test/experiment with code via the REPL.

chase-lambert20:01:09

that makes some sense. what has been holding me back from this workflow is...like if you could see my repl history it's just crazy how much I need to explore around to figure a function. If that was in my source code it would be dozens of iterations and lines i guess just for one function.

chase-lambert20:01:16

I keep trying to improve my paredit skills though so I can just keep adding to the same function more quickly or something.

seancorfield20:01:46

That's fine tho' -- while you're learning those (comment ..) forms would become a series "notes to self" about how you arrived at a particularly solution, that should help with retaining the knowledge about each REPL session...

chase-lambert20:01:49

now, i am just going through various books and tutorials. Maybe once I really start trying to create my project I wouldn't be jumping around so much.

chase-lambert20:01:15

good advice. I'll start playing around with that.

chase-lambert20:01:58

I find myself focusing too much on tooling and work flow instead of the actual exercises I should be doing though but I think I'm slowing getting better at the whole thing

seancorfield20:01:53

I find I "jump around" a lot less when I have a (comment ..) form containing all my notes/code about how I debugged a problem or built a solution -- and since it's all in the main editor window, I don't have to think about tooling or flow since I stay in one place and use one set of hot keys.

seancorfield20:01:41

I've used quite a few editors over my nearly nine years with Clojure and my workflow has settled into using just a small handful of commands: evaluate (current) form, evaluate top-level (enclosing) form, load file, run all tests in this namespace, run this test, show docs, show source. Plus connect to REPL/disconnect from REPL (which I actually start manually outside the editor -- and I tend to have three or four REPLs running constantly, in different projects).

seancorfield20:01:47

(lately I've added Cognitect's REBL to my workflow so I have alternatives for those two 'evaluate' commands that also submit them into REBL for visualization/data exploration)

lennart.buit20:01:52

huh, my comment blocks are usually short lived. I use them for debugging and delete them before committing

chase-lambert20:01:58

I like it. My other problem is testing out too many tools. I've tried to stay with emacs but it was locking up on my computer for some reason so I started exploring there too. I'm back to just trying to use emacs, cider, and using defaults for all of that as much as possible. Then I find a pain point while learning and let it take me off track by taking up all you folks time! hahaha

seancorfield20:01:48

These days I'm use Atom + Chlorine and Socket REPLs http://corfield.org/blog/2018/12/19/atom-chlorine/

hmaurer20:01:05

how’s it compared to what you were using before? protorepl?

seancorfield20:01:23

The main difference is that ProtoREPL used nREPL whereas Chlorine can connect to and upgrade a Socket REPL, which is really important for me.

hmaurer20:01:06

@seancorfield I’ve no idea what the difference between nrepl and a socket repl is πŸ˜…

seancorfield20:01:07

In terms of my core workflow, it's otherwise identical in Chlorine to what I did in ProtoREPL (partly because I submitted PRs to Chlorine for exactly the functionality I relied on from ProtoREPL πŸ™‚ )

chase-lambert20:01:29

I see good things happening there. That's one good problem in the clojure community I guess. Cool tools from all angles. I actually like emacs and keep my init.el minimal but cider (or I assume cider but it may have been a coincidence) was locking up my emacs completely randomly. But when I try Cursive (way too heavy for me) or VS Code (I like Calva) I find myself wasting too much time re-learning instead of focusing on the programming.

audiolabs20:01:38

@seancorfield not to start an 'editor war', but do you not have any issues with latency/speed of display or response time when opening files with Atom?

seancorfield20:01:21

Yeah, I keep dipping into VS Code and Calva -- really good work going on there. @audiolabs Atom can be a bit slow with large files (1,500+ lines) but otherwise I don't find it to be problematic.

hmaurer20:01:08

Oh, I use VSCode but didn’t know Calva

audiolabs20:01:57

Ok, that's my experience as well. However 1,500 lines isn't 'large' on some of the codebases I've had to work with. That's small to moderate 😞 (hooray C)

seancorfield20:01:13

Our codebase is all Clojure (well, we do still have some legacy CFML but that is rapidly getting replaced πŸ™‚ )

tim79220:01:02

I only notice a slow down after ~3500 lines of code in a file, however I’m sure other factors like the number of open tabs would have an impact - no? or maybe you have really long lines πŸ™‚

lennart.buit20:01:25

I like the git tools of VS Code quite a bit, something I miss in IntelliJ + Cursive

lennart.buit20:01:50

There are git tools, but they are not as intuitive as Gitlens + what is built in in VS Code

chase-lambert20:01:47

Do you folks not use keyboard based navigation? My problem with switching to things like vs code/calva is I want emacs keybindings for basic editing but then I have to constantly try to switch to accommodate the tool's defaults keybindings and conflicts.

seancorfield20:01:29

My key bindings (from ProtoREPL, now for Chlorine) on all platforms πŸ™‚

# temporary Chlorine key bindings:
'atom-workspace atom-text-editor:not([mini])':

  'ctrl-, y':       'chlorine:connect-clojure-socket-repl'
  'ctrl-, e':       'chlorine:disconnect'
  'ctrl-, k':       'chlorine:clear-console'
  'ctrl-, f':       'chlorine:load-file'
  'ctrl-, b':       'chlorine:evaluate-block'
  'ctrl-, B':       'chlorine:evaluate-top-block'
  'ctrl-, i':       'chlorine:inspect-block'
  'ctrl-, I':       'chlorine:inspect-top-block'
  'ctrl-, s':       'chlorine:evaluate-selection'
  'ctrl-, c':       'chlorine:source-for-var'
  'ctrl-, d':       'chlorine:doc-for-var'
  'ctrl-, x':       'chlorine:run-tests-in-ns'
  'ctrl-, t':       'chlorine:run-test-for-var'
/cc @audiolabs

audiolabs20:01:34

@chase-lambert what platform are you on?

audiolabs20:01:42

@chase-lambert there are ways to setup emacs-wide bindings depending on your setup, a quick google should give you some options

audiolabs20:01:55

then your editor selection becomes a bit more focused

chase-lambert20:01:01

I'm mostly just using a browser (w/ vimium even though I don't like vim modal editing normally), text editor, and terminal for 99% of my computing needs. I find the terminal actually maps with emacs keybindings perfectly. so it's just the text editor I need to solve

audiolabs20:01:10

I use emacs for probably 98% of my clojure/programming work

audiolabs20:01:21

very occasionally pop up a browser

chase-lambert20:01:42

Yeah, I think I'm just going to stay with it. The locking up thing had me worried but I can't recreate it.

dpsutton20:01:25

chat in #cider

dpsutton20:01:36

if that happens. i can think of a few things that do that

dpsutton20:01:08

long lines, some font locking stuff. but it should be easy to get rid of if it gets bad

chase-lambert20:01:46

awesome. I appreciate it. You know one meta thing on that that was concerning me is it seems the cider maintainer was getting burnt out. I almost feel bad going in the cider channel and taking up all his time and then it ends up being a stupid error on my part. The Calva guys seemed fresh so I started bugging them more. It sounds horrible but it's true!

dpsutton20:01:27

bbatsov just did a huge round of deep refactors moving nrepl versions, pretty printing stuff, and landing patches in boot, lein, for the nrepl stuff

chase-lambert20:01:40

Then I found out they use Cider too though so it's all a vicious cycle for these open source tooling folks

dpsutton20:01:57

and there are lots of others helping out just random questions. and i always try to help people find things in the source so they can easily become contributors

dpsutton20:01:06

yeah calva benefits from and benefits to the guts of CIDER. lots of the logic was extracted so other editors could use them without any nrepl dependencies or emacs dependencies

dpsutton20:01:35

there's also been a big change in the connection management that has had some rough edges while it was refined

chase-lambert20:01:48

it's such a great project. I just feel bad for all these folks. So much time and a majority of the errors are really just dumb user mistakes instead of a bug

dpsutton20:01:50

its not the most user friendly or discoverable. and there has been some frustrating breakages in the changes. lots of small things too. but the code is what volunteers make of it. so i've been trying to do my part for 3 years and encourage others to do so as well

dpsutton20:01:01

it's a fantastic community project wide open for contributors

chase-lambert22:01:43

minor idiomatic question: When writing a function with multiple arities how do you order it?

chase-lambert22:01:07

for example, this excercism solution:

(ns two-fer)

(defn two-fer
  ([name]
   (str "One for " name ", one for me."))
  ([]
   (two-fer "you")))

(two-fer)
;; => "One for you, one for me."
(two-fer "Chase")
;; => "One for Chase, one for me."

chase-lambert22:01:10

should I have the no arity first? I think it might be clearer to have the real intended functionality first in this case but not sure if there is a uniform way for this kind of thing

dpsutton22:01:56

i've usually seen it smaller arities on top. i have seen a few instances where the ones that "did work" versus the ones that supplied defaults were at the top

dpsutton22:01:21

i wouldn't fret it although i would have in some kind of order so its not a jump table

dpsutton22:01:25

(for human eyes)

chase-lambert22:01:08

that's what i'm thinking. I think it probably only makes sense in this case where you just want to recursively call back the function if no parameters are input.

chase-lambert22:01:09

what's a jump table?

noisesmith22:01:08

it's part of a programming technique where instead of a stack you use a lookup table and the output of a function to figure out what function to call next, point here is that it requires following a bunch of indirection mentally to see what's really going on

noisesmith22:01:48

(apologies if this isn't what @ was thinking of) -- the clojure trampoline function is designed to make jump tables nicer

dpsutton22:01:51

oh i just meant make them in some type of order. like usually the format of arities is 0, 1, 2, 3, 4 where four does work and each smaller arity exists to supply defaults

dpsutton22:01:07

just don't do 3124 or something crazy so you have to keep jumping around to see what they are calling

dpsutton22:01:07

i've also seen a 3 arity and a 2 arity where the 3 arity constructed things of a different shape for the 2 arity that did the work. it was confusing at first

dpsutton22:01:03

but honestly there are no real bad habits to pick up that your own sense of style won't cure you of. and if you do weird things they will be easy fixes or easy to ignore

cdimara23:01:54

How I create an alias to pass java_opts?

cdimara23:01:16

And are java_opts the same as jvm_opts?

cdimara23:01:35

I tried

{:aliases
       {:spiral 
          {:jvm-opts ["-D" "clojure.server.myrepl" "=" "\"{:port 5555,:accept,clojure.core.server/repl}\""]}}}

cdimara23:01:56

But it can't find the clojure.server.myrepl class

seancorfield23:01:24

replace the space between :port and 5555 with a comma , instead

seancorfield23:01:48

and it's all one string for JVM options

seancorfield23:01:50

:jvm-opts ["-Dclojure.server.myrepl={:port,555,:accept,clojure.core.server/repl}"]\

seancorfield23:01:37

(there's probably other useful stuff in that file for you, when learning about deps.edn)

cdimara23:01:22

Going to study it right now. Thank you.