Fork me on GitHub
#beginners
<
2021-02-16
>
Rowan Barnard05:02:17

Hi everyone, I've been using Clj commandline tools in Powershell on Windows and I entered the following into the commandline: user=> (require '[reader.snake :refer :all]) Execution error at user/eval213 (REPL:1). No namespace: reader.snake user=> (require '[examples.snake :refer :all]) nil user=> PS C:\Users\celap\Downloads\shcloj3-code\code> clj Clojure 1.10.2 user=> (require '[reader.snake :refer :all]) nil

Rowan Barnard05:02:39

Oops that was meant to be code blocked, anyway I am wondering why my first require of reader.snake didn't work but almost immediately after it did work

dpsutton05:02:53

have you got a link to the repo?

dpsutton05:02:25

you can create namespaces programmatically so its possible examples.snake does something like this

Rowan Barnard05:02:49

Ah I'm pretty beginner so not really understanding the repo thing - the code is on the page for the book in a zip file I will just link it

Rowan Barnard05:02:12

There's a deps.edn in the folder root that I added paths to src/reader and src/examples in

Rowan Barnard05:02:40

Because it wasn't seeing those earlier when I tried requiring in code from the book

seancorfield05:02:00

That's why you see the behavior above.

seancorfield05:02:35

You tried to require a ns whose file wasn't on the path. You updated deps.edn. You need to restart your REPL to pick up those changes.

Rowan Barnard05:02:01

But I had added the paths to the deps.edn yesterday and started the commandline and clj afresh today

Rowan Barnard06:02:22

I didn't do anything else outside the terminal between typing in those different commands on the terminal so it seems a bit mysterious to me, I am sure this has happened to me before too, but I've also done a lot of silly newbie things so maybe it is just that again 😅

dpsutton06:02:44

this repo is a little strange to me for some reason

dpsutton06:02:47

user> (require 'reader.snake)
CompilerException java.lang.Exception: No namespace: examples.import-static, compiling:(reader/snake.clj:1:1) 
user> (require 'examples.import-static)
nil
user> (require 'reader.snake)
nil
user> 

Rowan Barnard06:02:19

Ah yeah I think there was a mistake in the require statement in that file that I had to correct - just look at the require for the import-static and it should be obvious

Rowan Barnard06:02:44

I think it used round brackets instead of the square ones or something like that

Rowan Barnard06:02:12

And you may need to add the paths to the folders in the deps file as it has no paths in it

Rowan Barnard06:02:51

I added src/examples, src/reader and src to my paths

seancorfield06:02:38

The problem is that the source file src/reader/snake.clj has

(:refer examples.import-static :refer :all])
but it should be
(:require [examples.import-static :refer :all]))
Until you make that change, you cannot (require 'reader.snake)

seancorfield06:02:01

You don't need those paths added -- the deps.edn file is correct as it stands.

Rowan Barnard07:02:15

I did already change the refer to the proper require form after receiving an error about it and then after that it couldn't find the files, a FileNotFound I think, so that's when I added the paths to the deps and then after that it worked fine for me until today when that happened with requiring the reader.snake

Rowan Barnard07:02:04

I always seem to get weird things like this from time to time, maybe it is a caching thing again?

andy.fingerhut11:02:43

I am not sure if the following explains it, but if you do a require in a REPL that fails due to an error, there are some kinds of errors that still do cause the namespace to be created, and for the namespace to be added to a *loaded-libs* Clojure var. If you then do a requireagain with no special options, it will check *loaded-libs*, see the namespace is there, and not attempt to read the file again, unless you do a require with an option to force it to read the file again, e.g. (require 'examples.snake :reload), if I recall correctly.

andy.fingerhut11:02:38

*loaded-libs* is there to prevent the otherwise common occurrence that later namespaces will require namespace foo, causing foo to be loaded over and over again, including transitively everything that foo requires.

Rowan Barnard01:02:32

Ah I think that might be it then - I did try to require the reader.snake file but it failed because I'd accidentally left some test code in the file but had deleted the defs for it, so it was like a symbol couldn't be found error thrown from the require, then I deleted the test code from the file, tried the require again which is where the code snippet I pasted starts from, it failed to find the namespace which confused me as it was working fine the previous day and I'd added paths to the folder in my deps (which I apparently don't even need) so I tried requiring the book's examples.snake (which is basically a copy of the code in a parallel folder) and it worked fine so I scratched my head, reloaded Clj and then the require of reader.snake worked fine, so yeah sounds just like what you describe, so that must be it, thank you, and thank you also Sean 😉

Rowan Barnard01:02:26

This probably explains many of the other times where I've had problems with not being able to 'see' namespaces/files where I could've sworn it was working fine earlier, if anyone's writing books for Clojure beginners I think this is something that should definitely be mentioned 🙂

seancorfield01:02:43

This sort of thing is why it is so much better to work within an editor with a connected REPL, and then you aren't trying to type stuff into the REPL -- you're editing files and evaluating it directly into the running REPL.

Rowan Barnard01:02:58

Ah OK I see, many beginners' books if not the majority seem to assume working from the REPL though, so I think it would be a good idea to warn beginners about something like this

Rowan Barnard01:02:55

I learned something new though and reduced something magical to something I can understand, so feeling good thanks guys, wish you all a beautiful day 😉

seancorfield01:02:34

Yeah, I think a lot of authors don't want to deal with the vast array of editors and plugins and getting those setup and configured.

seancorfield01:02:12

Brave&True takes beginners through Emacs/CIDER setup -- which I don't think is a good idea (unless the beginner is already using Emacs perhaps!).

seancorfield01:02:02

Maybe as more and more developers use VS Code, we'll start to see material that assumes VS Code (& Calva). That's certainly a pretty good beginner setup and the maintainer team is super-responsive and helpful.

seancorfield01:02:29

What editor are you using @U014DSKAG9H? (I'm sure you've said before but I have a goldfish brain)

Rowan Barnard01:02:09

Ah yeah I'd say so, the VS Code numbers I saw on the last dev survey were huge, it seems to be rapidly approaching an industry standard, yeah I'm using Atom with your old REBL and Chlorine setup still and I occasionally use VS Code with Calva

Rowan Barnard01:02:09

I see Calva is getting some kind of interoperability with Clover now? So maybe I can use that setup with your new Clover config and Reveal?

seancorfield01:02:41

Yeah, I'm using Calva for everything except actual REPL eval stuff -- which I'm using Clover for. And I'm using Reveal.

seancorfield01:02:09

And that setup, for me, is identical on macOS (my work desktop) and Windows 10/WSL2/Ubuntu which is my personal laptop.

Rowan Barnard01:02:18

Oh that sounds cool, yeah I'll try switch to that sometime

Rowan Barnard01:02:36

Thanks for the links, bookmarking them now

seancorfield01:02:39

Installing Calva meant I could simplify my VS Code setup since it has its own paredit/rainbow parens etc -- so I stripped out three or four extensions after adding Calva. Then just edited Calva's settings to hide the nREPL UI (so I can use Clover's stuff instead).

seancorfield01:02:20

Also, the hot keys for Atom/Chlorine work for VS Code/Clover if you follow my setup, so you don't even have to learn new key bindings 🙂

Rowan Barnard01:02:21

Ah OK so the hide REPL ui is the only setting thing you have to change in Calva when using it with Clover?

Rowan Barnard01:02:04

Ah that is cool because I've grown accustomed to your keybindings and going back to Calva's felt really wrong 😛

seancorfield02:02:10

Do you switch between multiple machines at all?

Rowan Barnard02:02:02

No I just have the one desktop machine which may be close to 10 years old now 😂

Rowan Barnard02:02:19

It is an amd phenom 2

Rowan Barnard02:02:55

From back when the very first quad core CPUs were hitting the market

seancorfield02:02:59

Ah, OK. I asked because VS Code can automatically sync all your settings and all your extensions between all your machines 🙂 which I found very helpful when I went to set it up on a second machine (and it basically set itself up to match my first machine).

Rowan Barnard02:02:45

Oh yeah that's cool 🙂

Adrian Imanuel05:02:36

Hi, after tried several times, it still not connected, i figure out that i need 1. to define my :host-prefix "//FBI-HIDEOUT\\JUST_INTO_DS" 2. change my IPall port = 1433 3. make an inbound firewall for port 1433 (Instead I also turned my firewall off) but still giving an error, please help address where's the mistake? I'm using SQL express for an exercise btw.

Execution error (SQLServerException) at com.microsoft.sqlserver.jdbc.SQLServerException/makeFromDriverError (SQLServerException.java:234).
The TCP/IP connection to the host FBI-HIDEOUT, port 1433 has failed. Error: "Connect timed out. Verify the connection properties. Make sure that an instance of SQL Server is running on the host and accepting TCP/IP connections at the port. Make sure that TCP connections to the port are not blocked by a firewall.".

seancorfield05:02:07

I'm pretty sure "//FBI-HIDEOUT\\JUST_INTO_DS" is your problem.

seancorfield05:02:37

You shouldn't be mixing \\ and // I think.

Adrian Imanuel06:02:33

@U04V70XH6 how should i write it?

Adrian Imanuel06:02:20

(def db-sqlserver {:dbtype "sqlserver" :dbname "AdventureWorks"
                   :host-prefix "//FBI-HIDEOUT\\JUST_INTO_DS"
                   :port "1433"
                   :user "sa" :password "123456789"})

seancorfield06:02:56

Are you sure you need :host-prefix there? In the test suite for clojure.java.jdbc, the MS SQL test from macOS to Windows uses :host "127.0.0.1\\SQLEXPRESS" -- what is your setup with where you're running Clojure and where your MS SQL Server is running?

Adrian Imanuel06:02:35

my dependencies

:dependencies [[org.clojure/clojure "1.10.1"]
				 [com.layerware/hugsql-core "0.5.1"]
				 [com.layerware/hugsql-adapter-next-jdbc "0.5.1"]
				 [com.microsoft.sqlserver/mssql-jdbc "9.2.0.jre15"]
				 [mysql/mysql-connector-java "8.0.23"]
				 [honeysql "1.0.444"]
				 [seancorfield/next.jdbc "1.1.613"]]
I'm using windows 10, my MS SQL Server running from my own (not remote), i'm still practicing using SQL Express

seancorfield06:02:33

Why are you using :host-prefix? That's definitely not right (since :host will default to "127.0.0.1" and you don't want that weird string in front of that).

seancorfield06:02:51

If the DB is running on localhost, you probably want :host "127.0.0.1\\JUST_INTO_DS" (and remove :host-prefix).

seancorfield06:02:38

Not sure what FBI-HIDEOUT is in that...?

Adrian Imanuel06:02:49

(def db-sqlserver {:dbtype "sqlserver" :dbname "AdventureWorks"
                   :host "127.0.0.1\\JUST_INTO_DS"
                   :user "sa" :password "123456789"})
still give an error
Execution error (SQLServerException) at com.microsoft.sqlserver.jdbc.SQLServerException/makeFromDriverError (SQLServerException.java:234).
The TCP/IP connection to the host 127.0.0.1, port 1433 has failed. Error: "Connect timed out. Verify the connection properties. Make sure that an instance of SQL Server is running on the host and accepting TCP/IP connections at the port. Make sure that TCP connections to the port are not blocked by a firewall.".

Adrian Imanuel06:02:59

i'll try to make another server instead of JUST_INTO_DS

seancorfield06:02:17

That says that the JVM cannot connect to SQL Server running on localhost on the default port. Are you sure it's available on that IP address and not some other address?

seancorfield06:02:56

I see 192.168.137.1 in that screenshot you posted.

Adrian Imanuel06:02:23

i didn't make any changes, all is the default when establishing the server

seancorfield06:02:19

Then maybe try :host "192.168.137.1\\JUST_INTO_DS" is what I'm suggesting.

Adrian Imanuel06:02:15

can't as well 😭🙏 I've tried that.

seancorfield06:02:28

Then I have no idea how to help, sorry.

seancorfield06:02:49

You'll have to find someone who a) uses Windows and b) uses MS SQL Server.

Adrian Imanuel06:02:13

Sure no problem, you've helped me look into another different way... I'll try again... thank you so much.

seancorfield06:02:08

If you were on macOS or Linux, I'd suggest using telnet or nc to diagnose the network connectivity, outside of the JVM, but I've no idea what to suggest for Windows.

seancorfield06:02:10

I use WSL2 for all of my Clojure development on Windows (WSL2 running Ubuntu), and I use Docker for all of my MS SQL Server testing (and Microsoft's official Docker image for SQL Server is Linux-based).

Adrian Imanuel07:02:30

OH GOSH... imagine looking up & down, left & right, back & forth.. lol the answer is to "restart" the server... waow, worth 2 days 2 night... ahahahahaha :rolling_on_the_floor_laughing:

oly09:02:08

if I build a jar is there a way todo the equivalent of -m app.commands I have an app with a webserver but it also has cli-matic embeded so in essence want to be able to run one of the cli-matic commands with out clj available

noisesmith14:02:38

you can start clojure.main (the repl) from any jar with clojure: java -cp my.jar clojure.main

noisesmith14:02:06

this won't have nice things like command line editing / history etc. but it is enough to run any code that clojure knows how to find and run

noisesmith14:02:30

(ins)[email protected]:~/foo-uber$ java -cp target/uberjar/foo-uber-0.1.0-SNAPSHOT-standalone.jar clojure.main -m foo.bar
Execution error (FileNotFoundException) at clojure.main/main (main.java:40).
Could not locate foo/bar__init.class, foo/bar.clj or foo/bar.cljc on classpath.

Full report at:
/tmp/clojure-18226576097563274172.edn
(ins)[email protected]:~/foo-uber$ mkdir src/foo
(ins)[email protected]:~/foo-uber$ cat > src/foo/bar.clj
(ns foo.bar)

(defn -main [& args]
  (println "OK"))
(ins)[email protected]:~/foo-uber$ java -cp src:target/uberjar/foo-uber-0.1.0-SNAPSHOT-standalone.jar clojure.main -m foo.bar
OK
tl;dr - yes you can use -m with an uberjar

oly15:02:43

@U051SS2EU thanks for that info good to know that's possible I will give it a try 🙂

noisesmith15:02:05

I've used this technique in production, making one jar with many namespaces with -main functions to invoke, it can simplify things when a bunch of processes will all use the same deps

noisesmith15:02:50

(that is, not using anything outside the jar, but having multiple entry points in that jar that can be run - kind of like what busybox is for embedded *nix)

oly16:02:58

good to know I just wanted a quick way of running a maintenance task running in a pod so this should work nicely.

arielalexi12:02:28

Is there a way to keep the nano seconds for a timestamp when using #inst?

User > #inst "2002-02-19T03:40:27.000404Z"
;; => #inst "2002-02-19T03:40:27.000-00:00"

alexmiller12:02:36

no - the type being created there is ms precision

arielalexi12:02:46

can you reffer to more info about this?

alexmiller13:02:19

you could alternately construct https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html s which have ns resolution

popeye14:02:31

I have below string

Select * from table
WHERE 
 {
    a.name="John"
}

popeye14:02:02

I want to replace whatever comes from where

popeye14:02:13

how can i achieve in clojure

grazfather15:02:35

fwiw, my first little project in clojure was because I was cold in my apartment. https://twitter.com/Grazfather/status/1361688395806879749

🔥 3
clj 3
Mno15:02:30

That’s amazing honestly

Adrian Imanuel17:02:08

can clojure do that? woww....

grazfather15:02:32

Really simple: oz and an i2c library to read from the sensor.

grazfather15:02:41

and just slurp to hit the API

popeye15:02:56

I would like to split

Select * from table
WHERE 
 {
    a.name="John"
}

popeye15:02:07

as

Select * from table

popeye15:02:15

WHERE 
 {
    a.name="John"
}

popeye15:02:39

anything other than (clojure.String/split stmt #"WHERE")

noisesmith15:02:05

using string manipulation on SQL should be avoided, you can look at honeysql for example if you want to do generated queries

noisesmith15:02:30

this is the source of "sql injection attacks", one of the most expensive errors in the industry

👏 3
dpsutton15:02:32

it really depends on whether your question is recognizing a pretty static template (anything inside of the { and } braces) or parsing arbitrary sql

noisesmith15:02:30

for the latter, get an SQL parser, there must be multiple out there

noisesmith15:02:07

but just because you parse most input correctly doesn't make any transformed result safe to run against data that matters

noisesmith15:02:06

and regex operations are even worse

athomasoriginal15:02:53

An anonymous function is both: (fn [a] (do-something a)) and #(do-something %) - is there a name to differentiate the two “syntax styles”? I’m tempted to say “literal anonymous” v. “anonymous”.

andy.fingerhut15:02:17

The official Clojure docs call the #( ... ) syntax "anonymous function literal": https://clojure.org/reference/reader#_dispatch

andy.fingerhut15:02:05

I do not know of any special distinguishing name for a (fn [ ... ] ...) expression.

noisesmith15:02:16

one is a standard invocation (of a macro), the other is a reader macro expanding to the same special form

noisesmith15:02:41

(ins)user=> (macroexpand '(fn []))
(fn* ([]))
(ins)user=> '#()
(fn* [] ())

noisesmith15:02:08

the extra () is a side effect of how that reader macro works

athomasoriginal15:02:04

Nice and I’m thinking about new developers where for their benefit, it’s helpful to have a name to associate to a “style” to aid in their learning journey.

Mno15:02:09

I generally refer to it as the “anonymous function shorthand” as verbose as that may be 😅

athomasoriginal16:02:14

fair enough and makes sense!

rafalw17:02:35

Hi, I have such use case. I have map which is single point of truth, and I have module with functions which operates on this map, something like this:

(defn get-something [db ...] ...)
(defn update-something [db ...] ...)
...
And I have two use cases, first simple where, app is single threaded, second where app is mutlithtreaded and db is an atom. For multithreaded version I've created new module and wrapped every function from the first module in 'swap!', deref etc. for example
(defn update-something  [db ...] (swap! db a/update-something ...))
...
The issue is that, every time I add something to the first module, I have to write 'wrapper' in second module. Is there a way to avoid this duplication, and for example dynamically discover which version to use? Would multimethods be good fit here?

mruzekw17:02:58

Atoms are useful and useable in a single threaded environment. Could you use an atom for both cases?

rafalw17:02:19

I could, I'm just wondering if is there some 'elegant' solution, because in single threaded version using atom sounds like wasting cpu cycles (maybe I'm wrong about this....)

noisesmith18:02:42

atoms / swap! are indeed slow, you can use volatile! and vswap! in a single thread, or better yet use values as much as possible instead of holding and maintaining state. depending on app design / usage you could use an agent (guarentees that changes are serial but send returns before the change is applied) and/or some sort of queue consumer / data reducing pattern if the update patterns fit that

mruzekw17:02:23

I don't think there's much to worry about perf wise. If it becomes a problem, you can always switch it out later. For me, I'd see using an atom in both cases as an elegant solution.

andy.fingerhut17:02:26

In single-threaded environment unless you are doing these swap! calls millions of times per second, I doubt you'll notice a performance difference using it in a single-threaded program.

👍 3
andy.fingerhut17:02:08

In a single-threaded program, the overhead of swap! should be no more than one Clojure function call (the update function you pass to swap!), a pointer-equality check, and a JVM compare-and-swap operation that should be optimized to a single CPU instruction if it is in a hot code path.

💯 3
mruzekw17:02:12

Is there a way to grab the metadata of a passed in value?

(defn get-fragments [comp]
  (get-in (meta comp) [:helix/opts :apollo-cljs/fragments]))

mruzekw17:02:03

All the examples I see show use of the exact symbol plus a reader macro

(meta #'Username)

mruzekw17:02:54

Right now I'm trying to use the fn as (get-fragments Username) But I just realized maybe passing in #'Username would work instead. Doable, but not my ideal

andy.fingerhut17:02:23

If Username is a var that has whatever value you want to pass in to the function get-fragments, calling (get-fragments Username) passes the value of Username, but not the Var object itself.

andy.fingerhut17:02:22

Clojure collections can have metadata 'attached' to them, and if Username had a value that was say, a map, and that map had metadata attached to it, then calling (get-fragments Username) would cause the (meta comp) call inside of get-username to get the metadata attached to that map.

andy.fingerhut17:02:42

If the metadata you want is on the var #'Username, you can call (get-fragments #'Username) to pass the var to get-fragments, and then the (meta comp) call will get the metadata on the Var, if any.

mruzekw17:02:49

Okay thanks! Yes. Username is var (symbol made from a macro)

Illia Danko17:02:33

Hello folks. I have a nested 3 lvl data, such as [[[1 2] [3 4]] [[5 6] [7 8]]] . And I want to extract it to lvl 1 to become (1 2 3 4 5 6 7 8). I came up with

(->> [[[1 2] [3 4]] [[5 6] [7 8]]]
          (apply concat)
          (apply concat))
But what if the nesting will be more than 3 lvl in depth? And I don't like repeating my self with (apply concat) . Is there more functional (contrived :)) approach to tackling the issue?

noisesmith18:02:10

IMHO a data domain which treats multiple levels of nesting as equivalent to the flattened form is usually a design error, but flatten does do the specific thing you ask for

noisesmith18:02:57

it's often a brittle element though, because we often want to refactor code that took an item to instead take collection types and flatten makes that break

tvirolai17:02:05

You could use flatten

👍 6
Illia Danko17:02:18

that exactly what I need, thank you

Stuart18:02:07

flatten might work if you only have vector of vectors, lists etc but for example if you have something like a set:

(let [xs [[1 2] [3 4] [5 6] [7 #{8 9 19}]]]
    (flatten xs))

=> (1 2 3 4 5 6 7 #{19 9 8})
It won't work. AIUI, It will go into subitems if they return true for sequential? So just be aware of this.

Illia Danko09:02:51

@U013YN3T4DA good to know, anyway flatten fits well with my issue

Zak Singh20:02:12

Hi all, I’m having a bit of confusion regarding futures. I’m calling a process in the shell from my Clojure code and using a future to keep track of the streamed output. Here’s the code:

(defn start-server []
  (sh/proc "/bin/sh" "-c" "c8 node index.js"))

(defn stop-server [process]
  (let [output (future (sh/stream-to-string process :out))]
    (sh/destroy process)
    output))
When I run this, output has an error “Stream closed”. However, when I run these commands one by one in the REPL like so:
(def s (start-server))
(def o (future (sh/stream-to-string s :out)))
(sh/destroy s)
Then print out o, I correctly get the terminal output of the program between when I started and destroyed it. Is there something about using “let” in the first example which makes the future not evaluate until after the sh/destroy call has been made? “Stream closed” should only occur if I try to call stream-to-string after destroying the process, but I can’t see why that would be happening here

hiredman20:02:00

A future is a separate thread, evaluation happens in parallel

hiredman20:02:46

Once the new new threading is running there is no defined ordering relationship between what is being executed in your two threads, so what you have is a race condition

Zak Singh20:02:14

So in other words the sh/stream-to-string call might actually occur after the sh/destroy call on the other thread… makes sense. Is there a better way to go about what I’m trying to do? (run a program, store its output as its running, then kill it)

Zak Singh20:02:00

or at a smaller scale - enforce ordering here?

hiredman20:02:17

I guess the question is, why are you calling stream-to-string in a different thread from destroy, if you want destroy to be called after stream-to-string

hiredman20:02:36

There are ways to do that, but the simplest thing is to put them both on the same thread, and the simplest way to do that is to stop using future

Zak Singh20:02:00

I used the future because stream-to-string seems to block indefinitely since it’s waiting for the process to end (which it doesn’t since I have to kill it manually using destroy)

Zak Singh20:02:27

so from my understanding I need to run it on a separate thread so that I’m able to call destroy when I need to

hiredman20:02:34

So how do you know when you need to call destroy?

Zak Singh20:02:58

I’m writing a tool to fuzz a nodejs API, so it starts the API using c8 (which prints code coverage metrics to the terminal), runs a bunch of commands against the API, then shuts it off and inspects the output to view code coverage metrics. So it would be programmatic (but hard to illustrate here)

Zak Singh20:02:57

in reality this may not be a problem since the time between calling stream-to-string and destroy will be at least a few minutes, so the chance of a race condition would be virtually impossible

hiredman20:02:07

So put (when X (destroy ...))

hiredman20:02:27

The figure out X

Zak Singh20:02:22

alright perfect - thanks for the help. Sorry for the silly question, talking it out really helped clear things up