beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
seancorfield 2021-03-24T02:07:59.370700Z

You’re typing directly into the REPL?

seancorfield 2021-03-24T02:10:21.370900Z

(if you’re working from an editor, the REPL integration takes care of changing the REPL to the namespace you’re working in so code being evaluated has access to whatever is in that namespace — when you’re working with exploratory code in a Rich Comment Form (comment ..) for example)

Franco Gasperino 2021-03-24T02:11:32.371100Z

using your prior suggestion of vscode, that only occurs once i have performed a form eval from the target namespace. otherwise, the nrepl is sitting in clj.user

Franco Gasperino 2021-03-24T02:11:52.371300Z

if i eval a form in the editor, the namespace switches to the form's current ns

seancorfield 2021-03-24T02:11:58.371500Z

In the (very rare) cases that I’m typing directly into a REPL, I tend to stay in the user ns and require things in with an alias — following the same pattern as I would in code.

seancorfield 2021-03-24T02:12:03.371700Z

@franco.gasperino Yeah, most integrations assume you will eval the file before you start editing (some variant on “load file”, “evaluate file”, or whatever)

Franco Gasperino 2021-03-24T02:14:32.372Z

thats a reasonable default, but it can be what i'll call a tooling snag for newcomers

2021-03-24T02:15:16.372500Z

in-ns is reasonable for switching between namespaces that you have previously require'd

Franco Gasperino 2021-03-24T02:15:26.372600Z

i iterate quite a lot at the repl with form evals, and use those sessions as a basis for deftest later

seancorfield 2021-03-24T02:15:26.372800Z

If I’m writing a new file from scratch, the first thing I write is the ns form and it’s so habitual for me to hit the key to “eval top-level form” as I’m editing that the namespace is known to the REPL before I write any other code. For an existing file, I tend to just hit the key to load the file into the REPL as soon as I open it, if I think it hasn’t already been required. Depends on where I start editing.

seancorfield 2021-03-24T02:15:51.373Z

But my REPLs run for days so they tend to accumulate my entire codebase fairly quickly.

seancorfield 2021-03-24T02:16:36.373200Z

The REPL I’ve been using to work on HoneySQL v2:

user=> (dev/up-since)
#inst "2021-01-31T00:34:03.572-00:00"

Franco Gasperino 2021-03-24T02:17:13.373400Z

if the ns is re-bound in the repl, a load-file / top-level eval will discard to the on-file state?

seancorfield 2021-03-24T02:17:33.373600Z

I almost never type into a REPL — even exploratory eval I do inside a (comment ..) form in a source file. And then the code is already in my editor if I want to turn it into a test.

seancorfield 2021-03-24T02:17:49.373800Z

@franco.gasperino Not sure what you’re asking?

Franco Gasperino 2021-03-24T02:18:04.374Z

if in-file i have:

Franco Gasperino 2021-03-24T02:18:27.374200Z

(ns my-ns)
(def myvar 1)

Franco Gasperino 2021-03-24T02:19:04.374400Z

and at the repl, after eval'ing those 2 forms, i perform the following:

Franco Gasperino 2021-03-24T02:19:27.374600Z

clj:my-ns:> (def myvar 2)

Franco Gasperino 2021-03-24T02:19:56.374800Z

reloading / re-eval the in-file forms will rebind myvar to it's original state, yes?

Franco Gasperino 2021-03-24T02:20:20.375Z

e.g. discarding my in-repl binds

seancorfield 2021-03-24T02:20:44.375200Z

Namespaces are mutable. Only one instance of each namespace exists.

Franco Gasperino 2021-03-24T02:21:01.375400Z

got it

seancorfield 2021-03-24T02:21:25.375600Z

And your editor “REPL” and your console REPL are both clients into the same JVM and the same Clojure process.

seancorfield 2021-03-24T02:23:35.375800Z

An example from work: we run Socket REPLs in many of our production processes. If I VPN in and set up an ssh tunnel and then telnet into a production Socket REPL, I get a regular user=> prompt. If I do stuff in that session and then exit telnet and shut down the tunnel and VPN, then come back days later and connect back into that production process, everything I did in the past REPL session is still there in the user namespace.

Franco Gasperino 2021-03-24T02:24:55.376Z

following that rabbit hole, can the prod jvm process link live to a repl, and you as the remote user to the repl, and then fiddle (view, update, ...) an atom ref which the process is using?

Franco Gasperino 2021-03-24T02:25:27.376200Z

e.g. dump current application state (atom ref) to disk?

seancorfield 2021-03-24T02:26:31.376400Z

Yes, you have full access in the REPL to any global state in the running application.

Franco Gasperino 2021-03-24T02:26:43.376600Z

impressive

seancorfield 2021-03-24T02:27:32.376800Z

(and depending on how you built & deployed the app, you may even be able to redefine functions on the fly and have them have effect immediately — we’ve done that quite a few times with one of our internal apps to fix problems without needing downtime)

seancorfield 2021-03-24T02:28:23.377Z

When I’m developing locally, I have a REPL running, I connect my editor to it, and I build the app via the REPL — by eval’ing code in the editor — while the app is running.

seancorfield 2021-03-24T02:29:32.377200Z

I did presentations to both London Clojurians and Clojure Provo in the last few months showing how I build a web app directly via the REPL, modifying it while it is running. Even adding new libraries without restarting the REPL.

seancorfield 2021-03-24T02:30:12.377400Z

https://corfield.org/blog/2020/11/24/talks-clojures-superpower/ has links to the recordings.

Franco Gasperino 2021-03-24T02:30:29.377800Z

you know, i watched you give that on youtube a few days ago

Franco Gasperino 2021-03-24T02:30:48.378Z

with the cottage backdrop

seancorfield 2021-03-24T02:31:17.378200Z

My mum’s house in England 🙂

Franco Gasperino 2021-03-24T02:31:34.378400Z

beautiful lot

seancorfield 2021-03-24T02:33:00.378600Z

This shows a window where I started a REPL at the top. Then I connected to the same REPL in another window (below) and listed the available public vars. In the original REPL, I defined a var, then in the other REPL I referenced it. Just to show that even with multiple REPL clients, they “share” user:

seancorfield 2021-03-24T02:35:28.378800Z

OK, time to go feed the cats. I’ll be back online later!

Franco Gasperino 2021-03-24T02:35:34.379Z

thanks for the tips

cschep 2021-03-24T03:03:20.379400Z

DEPRECATED: Libs must be qualified, change compojure => compojure/compojure (deps.edn)

cschep 2021-03-24T03:03:32.379700Z

hey there, just added compojure to my deps.edn

cschep 2021-03-24T03:03:42.380Z

confused what this means? thought it seems to work

cschep 2021-03-24T03:04:20.380300Z

like i know i can change it to compojure/compojure but how would i have known that?

2021-03-24T03:05:29.380700Z

The deprecated message you quoted is the way I found out about it 🙂

cschep 2021-03-24T03:05:36.381100Z

ha, ok awesome

cschep 2021-03-24T03:06:05.382200Z

is it just double the name?

2021-03-24T03:06:10.382400Z

Leiningen and Clojure CLI tools have been permissive in allowing just an artifact name, without a group name, for many years.

2021-03-24T03:06:45.383400Z

e.g. Clojure's full name is org.clojure/clojure, where I believe org.clojure is the group name, and clojure is the artifact name within the group org.clojure.

2021-03-24T03:06:55.383800Z

Thisi is a Maven naming thing.

cschep 2021-03-24T03:07:05.384600Z

ahh

2021-03-24T03:07:22.385300Z

The Clojure CLI tools have recently deprecated the earlier common practice of leaving out the group name.

2021-03-24T03:08:03.386400Z

This earlier common practice was that if you didn't want to come up with a group name, then if your project's name wasn't already a group name on http://Clojars.org, then your project name also became the group name of that project.

2021-03-24T03:08:18.386800Z

(Caveat: This is my understanding, which could be off in several important details)

cschep 2021-03-24T03:09:25.387Z

that’s cool thanks

cschep 2021-03-24T03:09:38.387300Z

it looks like if you go search clojars they will tell you the right string to put in

cschep 2021-03-24T03:09:41.387500Z

deps.edn

cschep 2021-03-24T03:09:42.387700Z

which is nice

cschep 2021-03-24T03:09:44.387900Z

🙂

cschep 2021-03-24T03:10:59.389Z

so i’ve been hacking away at a single source file, core.clj which has a main function I don’t use, i’ve just been evaluating stuff in the REPL, i want to move some of this code to a differnet file and use this core to start a web server and then call into that other code

cschep 2021-03-24T03:11:08.389300Z

do i make a new file that has its own namespace?

cschep 2021-03-24T03:11:29.389800Z

is there a good thing i should read before i ask every individual question about this process?

2021-03-24T03:23:06.390600Z

I'm not sure of a good resource that covers this question, but would not be surprised if there was one.

2021-03-24T03:23:54.391700Z

You are on the well supported and easier to understand path if you put every Clojure namespace in a separate file, each beginning with an ns form with the name of that namespace, and the namespace name corresponds one-to-one with the file name it is stored in.

2021-03-24T03:25:55.394Z

Whenever you do require or use in Clojure, it searches all directories in your class path for one of a few file names that correspond with the namespace name, e.g. require of a namespace foo.bar.baz , and if your project's classpath contains the directory src (a common choice, but not mandated by Clojure), then it will look for a file named src/foo/bar/baz.clj

2021-03-24T03:27:37.395900Z

I would actually recommend against using a namespace ending with core, but it is a fairly minor reason -- if you ever get a stack trace because of some exception thrown in your code, it contains not the full path names of source files, but only the last part, e.g. bar.clj, not src/foo/bar/baz.clj in the stack trace lines, so if you have a namespace foo.core, any lines in a stack trace for that file will show up as core.clj, which is the same file name for many functions built into Clojure that likely also show up in your stack trace.

cschep 2021-03-24T03:28:46.396200Z

ah this is all very helpful, thank you!

cschep 2021-03-24T03:28:57.396800Z

is this java stuff or does clojure do its own file loading ?

2021-03-24T03:29:37.397900Z

Warning: you are certainly allowed to have dashes - in elements of your namespace names in Clojure. You must replace those dashes with underscores when you create the corresponding file or directory names in your source code, because that is what require and use look for.

cschep 2021-03-24T03:30:16.398400Z

ha, i bet that has cost some people some time

2021-03-24T03:34:45.400700Z

require is Clojure-specific, and the turning of dashes into underscores is Clojure-specific, because Clojure explicitly allows dashes in names, but Java does not allow dashes in class names. Part of the bottom of Clojure's require implementation uses a Java method getResource that searches the classpath, if I recall correctly: https://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String)

cschep 2021-03-24T03:35:45.400900Z

oh awesome

cschep 2021-03-24T03:36:01.401200Z

I appreciate the explanation

cschep 2021-03-24T03:36:14.401400Z

https://github.com/abedra/shouter

cschep 2021-03-24T03:36:26.401900Z

I’m copying the bones of this

2021-03-24T03:37:26.403Z

The naming of a file core.clj is common in Clojure projects, and very well might trace back to the use of core.clj in Clojure's implementation, and/or the creation of a file core.clj when Leiningen creates a template project for you in its default template.

2021-03-24T03:38:11.403700Z

I'd recommend feeling free to rename that file and namespace in any such template.

NoahTheDuke 2021-03-24T04:04:21.408900Z

Through habit or whatever, I’m tied to debugging/developing using println. I like being able to see the changes to data over time which I don’t get when only seeing output from evaluating forms in a comment block

seancorfield 2021-03-24T04:05:44.410700Z

Regarding the lib name thing (`compojure` => compojure/compojure) there is another change which folks need to be aware of if they plan to publish a library (to Clojars; Maven is already stricter). Clojars historically allowed "any" group ID so a lot of early libraries just picked a single name, like ring or compojure or hiccup, and that became the group ID as well as the artifact ID (these are Maven terms really). For security reasons, Clojars is implementing a policy where group IDs must be reverse domain name format -- which is already common in the Java world -- and you must be able to verify that the reverse domain name belongs to you. By default, they offer net.clojars.<username> which everyone can use (because your username is "verified" by virtue of you logging into Clojars). In addition, if you use your GitHub ID to login to Clojars, you can use com.github.<username> for your group (verified by virtue of you logging into GitHub to authenticate).

seancorfield 2021-03-24T04:07:07.411200Z

There's a good thread about this on ClojureVerse if you're interested in more about it: https://clojureverse.org/t/clojars-verified-group-names/7297

seancorfield 2021-03-24T04:09:41.414Z

When I started publishing libraries, I chose seancorfield as my group ID but I am slowing migrating my libraries to com.github.seancorfield. If you use the CLI and deps.edn, and you create projects with clj-new, the latest version tries hard to create library projects that will conform to the new security policy at Clojars. If you ask clj-new to create you a new project with the name <username>/<projectname> then it will assume you mean com.github.<username> for the group ID and will set you up to publish your library that way.

cschep 2021-03-24T04:09:46.414200Z

that’s really interesting

cschep 2021-03-24T04:10:03.414500Z

where does clojars end and maven begin?

seancorfield 2021-03-24T04:11:53.416500Z

Maven has much tighter control over what gets published and how. Clojure itself is published to Maven, along with all the "Contrib libraries" (everything under the org.clojure group). Clojars is maintained by the Clojure community (funded by Clojurists Together and various companies) and allows "everyone" to publish libraries there. Leiningen, Boot, and the Clojure CLI all know to look on Maven for a library and if it isn't there they look on Clojars for it.

seancorfield 2021-03-24T04:11:58.416700Z

(by default)

cschep 2021-03-24T04:21:26.416900Z

ah that’s cool

cschep 2021-03-24T04:21:40.417300Z

you don’t really have to know where the thing you want is

cschep 2021-03-24T04:22:02.417800Z

i’ve been using vscode to start a REPL and jack-in

cschep 2021-03-24T04:22:30.418Z

but i’d like to learn how it actually works so i’m following

cschep 2021-03-24T04:22:30.418200Z

https://www.clojure.org/guides/deps_and_cli

cschep 2021-03-24T04:22:46.418500Z

clj -X schepball.main/-main

cschep 2021-03-24T04:22:48.418700Z

is this right?

dpsutton 2021-03-24T04:23:44.419700Z

doesn't look like it. -X needs to call a function that takes a single map. -main is usually a function that accepts a sequence of string args. the two aren't really comptible

cschep 2021-03-24T04:24:34.419900Z

ahh

seancorfield 2021-03-24T04:28:04.422100Z

-main is generally invoked via "main opts" which means -M and passes zero or more strings to the function (which has [& args] for arguments); executing functions via -X which pass a single hash map to the function.

NoahTheDuke 2021-03-24T04:28:20.422400Z

@seancorfield to pull out a part of the earlier conversation, how do you handle branches when developing in long-running repl sessions?

NoahTheDuke 2021-03-24T12:04:38.443500Z

That’s super interesting! Thanks for the explanation.

cschep 2021-03-24T04:29:54.422600Z

ah that is interesting

cschep 2021-03-24T04:30:51.423100Z

i’m finding myself wishing for a clj run that new how to run my “main” file

phronmophobic 2021-03-24T04:32:11.423200Z

I wrote a small utility with a similar idea, https://github.com/phronmophobic/clirun Args are parsed as edn, but you can specify the main function. For example:

# Write to a file using clojure.core/split
$ clj -M:run clojure.core/spit '"foo.txt"' '[1 2 3]'
$ cat foo.txt
[1 2 3]

seancorfield 2021-03-24T04:32:36.423500Z

clojure -M -m my.namespace isn't much work though...

cschep 2021-03-24T04:34:23.423700Z

ah i didn’t know about that flag

cschep 2021-03-24T04:34:35.423900Z

as usual i should read more before asking questions 🙂

cschep 2021-03-24T04:35:59.424100Z

the -m flag makes sense to me I think

cschep 2021-03-24T04:36:08.424300Z

but the -M i’m not understanding

cschep 2021-03-24T04:36:31.424500Z

it uses clojure.main?

seancorfield 2021-03-24T04:36:41.424700Z

It's a good question! Maybe it's our working style at World Singles Networks but I don't find it's a problem. My REPL state just followed the code that I'm working on, so when I branch for a feature/bugfix, I just keep working in the REPL, until I'm done and push the branch for a PR review. If I need to work on something that builds on that, I'll cut the new branch off the previous branch rather than the trunk, and keep working. If it's orthogonal work, the changed functions in the REPL won't affect it anyway, even I start over from the trunk.

dpsutton 2021-03-24T04:36:41.424900Z

and clojure -X core/one-of-my-tasks works just as well. can only be a single main in a file, whereas -X can start with any function taking a single map as an arg

dpsutton 2021-03-24T04:37:28.425100Z

(doc clojure.main/main)
-------------------------
clojure.main/main
([& args])
  Usage: java -cp clojure.jar clojure.main [init-opt*] [main-opt] [arg*]

  With no options or args, runs an interactive Read-Eval-Print Loop

  init options:
    -i, --init path     Load a file or resource
    -e, --eval string   Evaluate expressions in string; print non-nil values
    --report target     Report uncaught exception to "file" (default), "stderr",
                        or "none", overrides System property clojure.main.report

  main options:
    -m, --main ns-name  Call the -main function from a namespace with args

seancorfield 2021-03-24T04:39:05.425300Z

Most of our branches are fairly short-lived. We merge everything to the trunk as soon as it is reviewed and approved. And we deploy to staging for business review almost every day (sometimes several times a day), and from there almost all apps can go to production completely automatically as many times a day as business want (the business team can deploy from staging to production by checking a box and clicking a button, even for database migrations).

seancorfield 2021-03-24T04:40:19.425500Z

If I have to go back and forth between two branches that actually do "interfere" a "reload all" generally re-syncs the state (literally just (require 'main.ns :reload-all) -- even while the app is running).

dpsutton 2021-03-24T04:40:34.425700Z

so with -M you're just creating arguments for clojure.main/main. And one of those arguments is -m which expects a ns-name, and it will call the -main function of that ns with any command line args

seancorfield 2021-03-24T04:43:12.425900Z

-M can have other arguments: just a script name, for example:

seanc@DESKTOP-30ICA76:~/clojure$ cat > script.clj
(println "Hello, World!")
seanc@DESKTOP-30ICA76:~/clojure$ clojure -M script.clj
Hello, World!
or -e to evaluate a Clojure form:
seanc@DESKTOP-30ICA76:~/clojure$ clojure -M -e '(println "Hello, Command-Line
!")'
Hello, Command-Line!
as well as -m to specify a namespace whose -main function should be invoked.

👆 1
seancorfield 2021-03-24T04:44:28.426200Z

Take a look at tap> which lets you debug by sending values to specific "listeners" such as Portal or Reveal (or Cognitect's REBL).

seancorfield 2021-03-24T04:44:55.426400Z

Or you can just (add-tap println) and it'll behave like print debugging.

seancorfield 2021-03-24T04:45:14.426600Z

But the nice thing is that you can leave tap> in your code and add and remove listeners any time you need to debug.

cschep 2021-03-24T04:45:22.426800Z

oh interesting

cschep 2021-03-24T04:45:30.427Z

looks like you can skip the -M but you get a warning

cschep 2021-03-24T04:45:36.427200Z

WARNING: When invoking clojure.main, use -M

cschep 2021-03-24T04:45:37.427400Z

😄

seancorfield 2021-03-24T04:45:49.427600Z

Yes. In the future, you'll have to use -M for clojure.main.

cschep 2021-03-24T04:45:55.427800Z

easing us into it

seancorfield 2021-03-24T04:46:29.428Z

Right now, -A will also run clojure.main and :main-opts (in deps.edn aliases) but that will change at some point and it will only start a REPL.

seancorfield 2021-03-24T04:46:58.428200Z

(and if you use -A for main stuff, you'll get a warning that you should use -M instead 🙂 )

cschep 2021-03-24T04:47:30.428400Z

so you should use -M for aliases?

seancorfield 2021-03-24T04:48:31.428600Z

-X, -M, and -A all accept aliases and combine them and then a) exec a function, b) run clojure.main, c) start a REPL with those aliases.

seancorfield 2021-03-24T04:49:29.428800Z

I'm just learning about Crux, so I just started a REPL with this command:

seanc@DESKTOP-30ICA76:~/clojure$ clojure -Sdeps '{:deps {juxt/crux-core {:mvn/version "RELEASE"}}}' -M:rebel:reveal:add-libs:dev/repl
(based on aliases in my dot-clojure repo's deps.edn file and the dev.clj startup script).

cschep 2021-03-24T04:50:39.429Z

is dev is the file and repl is the function in it?

seancorfield 2021-03-24T04:50:43.429200Z

That starts Rebel Readline as my interactive REPL, starts Reveal for tap>'ing data into to visualize it, the add-libs alias brings in a branch of tools.deps.alpha that lets me add new dependencies without restarting my REPL, and :dev/repl runs my dev.clj script.

seancorfield 2021-03-24T04:50:53.429400Z

No, :dev/repl is just a keyword, an alias.

cschep 2021-03-24T04:51:00.429700Z

ah ok

cschep 2021-03-24T04:51:27.430200Z

cool that is really helpful

cschep 2021-03-24T04:51:55.430400Z

oh so you have aliases that you can use on any project?

seancorfield 2021-03-24T04:52:11.430600Z

Yes, in ~/.clojure/deps.edn

seancorfield 2021-03-24T04:52:35.430800Z

(on an XDG setup it's in ~/.config/clojure/deps.edn I believe)

cschep 2021-03-24T04:53:04.431Z

that’s cool, lots to learn!

seancorfield 2021-03-24T04:53:07.431200Z

My :dev/repl alias -- via the dev.clj script -- also starts a Socket REPL so I can connect VS Code to it (using Clover).

cschep 2021-03-24T04:53:25.431400Z

i’ve seen you mention that a few times, what is the difference between a socket repl and nrepl?

seancorfield 2021-03-24T04:53:54.431600Z

Socket REPL is built into Clojure and has no dependencies at all. So we run them in several production processes.

seancorfield 2021-03-24T04:54:32.431800Z

You just specify a JVM option when starting up a Clojure program.

seancorfield 2021-03-24T04:54:52.432Z

But most editor tooling expects nREPL. Hence I use Clover which supports Socket REPL instead.

seancorfield 2021-03-24T04:55:08.432200Z

But you can also connect via telnet directly to a Socket REPL 🙂

cschep 2021-03-24T05:00:44.432400Z

ha, awesome

Franco Gasperino 2021-03-24T05:29:44.432600Z

tap> is pretty slick

Franco Gasperino 2021-03-24T05:31:45.432800Z

would be great if tap> returned its argument

Franco Gasperino 2021-03-24T05:33:14.433100Z

would compose a bit better

seancorfield 2021-03-24T05:52:38.433300Z

There's probably a tap-> coming in 1.11.

seancorfield 2021-03-24T05:53:10.433500Z

You can use (doto tap>) today for that.

seancorfield 2021-03-24T05:53:36.433700Z

(-> 42 (doto tap>) inc (doto tap>)) will tap 42, increment it, and then tap 43.

Franco Gasperino 2021-03-24T05:54:17.433900Z

another discovery. only had seen (do)

seancorfield 2021-03-24T05:55:27.434100Z

doto applies functions to its argument and then returns the argument itself. Mostly used for mutable (Java) objects, but I've often dropped (doto println) into a -> expression to print an intermediate value and also returns it (`println` returns nil).

seancorfield 2021-03-24T05:56:23.434300Z

dev=> (doto 42 (println 1) (println 2))
42 1
42 2
42
So 42 becomes the first argument to each println call, like -> threading.

Franco Gasperino 2021-03-24T05:56:42.434500Z

yes, handy

Franco Gasperino 2021-03-24T05:56:52.434700Z

i wanted to (tap>) in a thread-after series

Franco Gasperino 2021-03-24T05:57:55.434900Z

->> col 
(work)
(tap>)
(work)
(tap>)
doto should work for that

seancorfield 2021-03-24T05:58:21.435100Z

You can always start with -> and use ->> inside it as needed...

seancorfield 2021-03-24T05:59:29.435300Z

dev=> (-> [1 2 3 4]
 #_=>     (doto tap>)
 #_=>     (->> (map inc))
 #_=>     (doto tap>)
 #_=>     (->> (filter even?))
 #_=>     (doto tap>))
(2 4)

Franco Gasperino 2021-03-24T06:00:04.435500Z

i immediately ran into the thread after

defn node-valid? [schema event]
  (->> schema
    (map (fn [[k v]] (leaf-valid? k v event)))
    (filter false?)
    (first)
    (nil?)))

Franco Gasperino 2021-03-24T06:00:25.435700Z

attempting a decending map validator

seancorfield 2021-03-24T06:00:57.435900Z

or do this:

dev=> (->> [1 2 3 4]
 #_=>      (#(doto % tap>))
 #_=>      (map inc)
 #_=>      (#(doto % tap>))
 #_=>      (filter even?)
 #_=>      (#(doto % tap>)))
(2 4)

Franco Gasperino 2021-03-24T06:01:05.436100Z

i have a map that's the allowed schema, and an event which i'm validating against the schema

Franco Gasperino 2021-03-24T06:01:20.436300Z

yea thats useful

Franco Gasperino 2021-03-24T06:01:59.436500Z

now looking at tail recursion and how do decend to N child nodes

Franco Gasperino 2021-03-24T06:02:04.436700Z

*descend

Franco Gasperino 2021-03-24T06:03:47.436900Z

oh the (comment) tip is great

Franco Gasperino 2021-03-24T06:04:06.437100Z

next i want to get the repl into debug mode so i can use line breaks

Franco Gasperino 2021-03-24T06:08:31.437300Z

i was pleasantly surprised when i read that i could run a socket repl on a container. That will allow me to design a cljs UI, allow input from the user as edn, have the cljs ship that to a socket repl to load-file or eval for correctness

Franco Gasperino 2021-03-24T06:08:37.437500Z

crossing hosted runtime

Franco Gasperino 2021-03-24T06:10:19.437700Z

can then validate the user-supplied edn, display some feedback on the ui, and optionally save the edn to a loadable file for later

Franco Gasperino 2021-03-24T06:11:03.437900Z

code = data, data = code really is powerful

zackteo 2021-03-24T06:27:39.440Z

Hi Everyone, may I ask what is the most common way to transmit data from Clojurescript to Clojure, vice-versa, via HTTP server? From what I understand is that EDN is converted to JSON and back? And another way is things like transit ?

seancorfield 2021-03-24T06:28:15.440100Z

“next i want to get the repl into debug mode so i can use line breaks” — not sure what you mean by this?

seancorfield 2021-03-24T06:29:16.440300Z

I think both EDN and Transit are fairly common. EDN is a bit easier, Transit is generally a bit faster (esp. if you have larger amounts of data?).

zackteo 2021-03-24T06:30:57.440500Z

Can I just use ring to give a "application/edn" response?

zackteo 2021-03-24T06:31:18.440700Z

Do you know if there are any resources I can look at for this? 🙂

zackteo 2021-03-24T06:35:18.440900Z

Okay i think this might be good https://swannodette.github.io/2014/07/26/transit-clojurescript/

💯 1
Franco Gasperino 2021-03-24T06:46:27.441100Z

vscode claims it can support line breaks during form evaluation. not sure how much its needed now with tap>

2021-03-24T12:34:54.444400Z

Why would I use something like a record without a body?

2021-03-24T12:35:12.444900Z

(defrecord MyRecord [a b c])

2021-03-24T13:03:10.445400Z

You might want to extend a protocol to support structured record

popeye 2021-03-24T14:19:27.447300Z

I was referring this file https://medium.com/@dashora.rajnish/how-to-create-apis-in-clojure-supporting-file-upload-and-data-transformation-using-ring-and-ad40fc3ca2d0 and I found request created as below format `

{...
 :params
  {"file" {:filename     "sample.csv"
           :content-type "text/csv"
           :tempfile     #object[java.io.File ...]
           :size         51}}
...}
How can I pass value to the temp file while writing testcases?

popeye 2021-03-24T14:20:59.447800Z

how can i test post request while uploading file in clojure

2021-03-24T14:58:03.449200Z

Can someone tell me what I'm doing wrong here:

(defn build-map [acc [{:keys [hb-id status group]}]]
  (if (some? (acc hb-id))
    (update-in acc [hb-id :groups] conj group)
    (assoc acc hb-id {:status status :groups #{group}})))

(let [data [{:hb-id 1 :status "ERR" :group 1}
            {:hb-id 1 :status "ERR" :group 2}
            {:hb-id 2 :status "OK" :group 1}
            {:hb-id 3 :status "INFO" :group 1}
            {:hb-id 4 :status "WRN" :group 2}
            {:hb-id 5 :status "OK" :group 2}]]
  (->> (group-by :hb-id data)
       (reduce build-map {})))
I'm trying to turn data into
{1 {:status "ERR" :groups #{1 2}}
 2 {:status "OK" :groups #{1}}
 3 {:status "INFO" :groups #{1}}
 4 {:status "WRN" :groups #{2}}
 5 {:status "OK" :groups #{2}}}
Where it's a map keyed on hb-id with each group in a set of groups.

2021-03-24T14:59:59.450Z

I think its my update-in I'm not understanding

2021-03-24T15:00:55.450700Z

But I tried this, and this works:

(let [hb    2
      group 3
      data  {1 {:status "ERR" :groups #{1 2}}
             2 {:status "OK" :groups #{1}}
             3 {:status "INFO" :groups #{1}}
             4 {:status "WRN" :groups #{2}}
             5 {:status "OK" :groups #{2}}}]
  (update-in data [hb :groups] conj group))
=> 
{1 {:status "ERR", :groups #{1 2}},
 2 {:status "OK", :groups #{1 3}},
 3 {:status "INFO", :groups #{1}},
 4 {:status "WRN", :groups #{2}},
 5 {:status "OK", :groups #{2}}}

dpsutton 2021-03-24T15:02:35.451700Z

group-by returns a map of {grouping [items]}. But in your reduce you're only expecting [items].

dpsutton 2021-03-24T15:03:06.452200Z

ie, calling first on your group by yields [1 [{:hb-id 1, :status "ERR", :group 1} {:hb-id 1, :status "ERR", :group 2}]] which is a different shape than expected by your reducing function

2021-03-24T15:03:33.452800Z

ah!! Yes, I see! Thanks. I'll have a rethink of how to do this, maybe group-by isn't the way to go

dpsutton 2021-03-24T15:04:11.453200Z

yes i think you could more easily just reduce over your input data

2021-03-24T15:04:16.453400Z

yeah, i think so!

dpsutton 2021-03-24T15:05:31.454200Z

well maybe not. you're almost there if you build a function that turns [{:hb-id 1, :status "ERR", :group 1} {:hb-id 1, :status "ERR", :group 2}] into {:status "ERR" :groups #{1 2}} i guess. although i haven't looked at the consistency of your data

2021-03-24T15:07:40.454800Z

This seems to work, I had the syntax wrong for desttructuring as well, had an uncessary [] around the {:keys}

(defn build-map [acc {:keys [hb-id status group]}]
  (if (some? (acc hb-id))
    (update-in acc [hb-id :groups] conj group)
    (assoc acc hb-id {:status status :groups #{group}})))

(let [data [{:hb-id 1 :status "ERR" :group 1}
            {:hb-id 1 :status "ERR" :group 2}
            {:hb-id 2 :status "OK" :group 1}
            {:hb-id 3 :status "INFO" :group 1}
            {:hb-id 4 :status "WRN" :group 2}
            {:hb-id 5 :status "OK" :group 2}]]
  (reduce build-map {} data))
Thanks for your help!

dpsutton 2021-03-24T15:08:09.455100Z

that looks even more wrong to me

dpsutton 2021-03-24T15:08:24.455400Z

ah nevermind. the group by is gone

2021-03-24T15:10:15.455900Z

why did it let me do this?

(defn foo [acc [{:keys [bar quax]}]]
  
  )

(reduce foo {} {:bar :quax})
=> nil
What is it expecting there for the destructuring being in a vector? As opposed to:
(defn foo [acc {:keys [bar quax]}]
  
  )

dpsutton 2021-03-24T15:13:04.456400Z

destructuring is as recursive as the structure. (let [[[[[foo]]]] [[[[3]]]]] foo) returns 3

dpsutton 2021-03-24T15:13:32.457Z

the binding forms to some extent mimc the shape of what you are destructuring

2021-03-24T15:13:41.457200Z

I guess the question is about why it is not barfing

2021-03-24T15:14:06.457600Z

It is a legal destructuring form, if you want to destructure a map as a first element of a vector given as a parameter

👀 1
2021-03-24T15:15:04.458900Z

ok! Yeah, i was expecting it to either not compile or throw

2021-03-24T15:15:06.459100Z

And not only a vector, but any sequential thing. Perhaps it is calling seq on the map given as a parameter? Not sure.

dpsutton 2021-03-24T15:17:09.460400Z

i think what happened is that key-value pairs are associative and index, so that allowed []. and the hb-id was not destructured against the :keys thing. There seems to be some safety added in

dpsutton 2021-03-24T15:17:50.461100Z

(let [{:keys [a]} 1]) throws an error about type hinting a primitive. but the same thing inside vectors (let [[{:keys [a]}] [1]] a) returns nil

JohnJ 2021-03-24T17:14:30.469300Z

Is it common to convert seqs to vectors regularly? is the performance cost high? I find myself doing it frequently to be able to randomly grab elements from a seq. Limiting myself to small subset of contructs from clojure.core to be able to keep vectors as vectors doesn't feel right

grazfather 2021-03-24T17:17:40.469700Z

I think random access to a seq is the weird part

grazfather 2021-03-24T17:18:25.470400Z

it’s got to be at least O(n) to convert, with a bunch of frees and allocs by my guess

dpsutton 2021-03-24T17:18:43.470700Z

use the datastructure that provides efficient operations that you need

JohnJ 2021-03-24T17:21:02.471Z

they get converted to seqs 😉

dpsutton 2021-03-24T17:22:14.471700Z

can you give an example? we can give some options that might help out

➕ 1
JohnJ 2021-03-24T17:26:37.473700Z

well, I know how to keep vectors as vectors but most functions work on seqs, so you have have a limited API to keep vectors as vectors, why kind of example do you have in mind?

JohnJ 2021-03-24T17:27:05.474100Z

Isn't it kind of obvious?

dpsutton 2021-03-24T17:27:32.474600Z

not particularly. can you give an example in your actual usage where you found yourself converting a seq to a vector?

dpsutton 2021-03-24T17:28:02.475200Z

I suspect i know the gist of what you're talking about but want a concrete case to delve into it

2021-03-24T17:29:31.477400Z

the answer is basically "don't write programs that mix mapping and filtering with random indexing", so @dpsutton's question is at a high level "can you show us your program that mixes mapping and filtering with random indexing so we can help you transform it into one that doesn't do that"

2021-03-24T17:30:09.478Z

ideally your operations on seqs form a kind of pipeline

2021-03-24T17:30:55.478600Z

so that if you must convert back and forth, you have that on each end of a pipeline of a lot of operations

seancorfield 2021-03-24T17:32:59.480Z

I’m curious about the problem space where you are “frequently .. grab[bing] elements from a seq”, i.e., where you need random access, in a context of processing seqs (mapping and filtering) @jjaws?

JohnJ 2021-03-24T17:36:23.482100Z

@hiredman yes, that's what I'm doing right now, doing lots of data massaging using sequence functions then converting to vectors at the end of each pipeline for random access

dpsutton 2021-03-24T17:36:47.482900Z

i was going to bring up transducers as well where you can control things a bit more

2021-03-24T17:37:28.483900Z

than it sort of depends on the pipeline

dpsutton 2021-03-24T17:37:44.484600Z

yes. which is why i wanted a concrete example

JohnJ 2021-03-24T17:38:04.485100Z

@seancorfield working with lots of CSV stuff, transforming it, then random access on fields

seancorfield 2021-03-24T17:38:53.486300Z

Can you be a bit more specific? I’d expect CSV data to end up as a sequence of hash maps, and then each row can have O(1) access to specific fields by name.

JohnJ 2021-03-24T17:39:30.486900Z

@dpsutton yep, transducers help with the 'no multiple passes over the data'

2021-03-24T17:40:09.487500Z

not really, transducers and a lazy seq pipeline will have the same number of passes

dpsutton 2021-03-24T17:40:15.487800Z

hiredman has a gist for this purpose here.

dpsutton 2021-03-24T17:40:29.488500Z

you want to be able to query this as a database essentially

2021-03-24T17:41:33.489900Z

transducers will remove some object allocation overhead for seqs, and will turn your pipeline into a very lean vector -> vector transformation using something like into

dpsutton 2021-03-24T17:41:40.490Z

i'm reading through tim baldridge's build yourself a logic engine that creates a db like this as well.

JohnJ 2021-03-24T17:41:56.490300Z

@hiredman doesn't it avoid intermediate sequences?

2021-03-24T17:42:01.490500Z

it does

2021-03-24T17:42:15.490900Z

but intermediate sequences are traversed all at once

2021-03-24T17:42:40.491300Z

unless you are traversing intermediate results for some reason

JohnJ 2021-03-24T17:44:28.492100Z

@seancorfield don't see the difference, both are associative, maps will still be converted to seqs

2021-03-24T17:46:02.493800Z

@jjaws I suspect @seancorfield is thinking about (map f all-rows-in-csv) and you are thinking about (map f a-row-in-a-csv)

JohnJ 2021-03-24T17:47:48.495100Z

oh ok, yes, was thinking of a-row-in-a-csv vectors vs maps

2021-03-24T17:47:59.495200Z

and this is kind of why a more concrete example can help

JohnJ 2021-03-24T17:49:16.496500Z

I'll try come to up with something small but not so contrived

dpsutton 2021-03-24T17:53:13.497200Z

if you need a query language over your csv, you could look into datascript or your own hand rolled triple store or set/index can be useful as well

Franco Gasperino 2021-03-24T20:24:05Z

regarding clojure.spec, it would reason that the more common use cases would be for library design as well as external input validation. am i correct with this assumption?

seancorfield 2021-03-24T20:39:13.000800Z

https://corfield.org/blog/2019/09/13/using-spec/ — we’ve been very heavy users of Spec in production as well as dev/test since it first appeared.

sova-soars-the-sora 2021-03-24T20:55:08.001200Z

learning via lurking... 🙂

Aldo Nievas 2021-03-24T21:11:42.001400Z

Hi all ! newbie here! anyone using neovim 5.0 and clojure-lsp ? some pretty basic config in lua ? thanks in advance.

robertfw 2021-03-25T23:04:49.059600Z

if you haven't already you may want to try #lsp and/or #vim

sova-soars-the-sora 2021-03-24T21:12:10.001700Z

welcome 🙂