clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
seancorfield 2021-01-06T00:00:51.465900Z

True, running a local REPL instance on a production server, that can spin up the same DB connections etc, is "safer" in terms of not letting you accidentally blow up a running production process. Well, at least not letting you redefine the code -- you can still blow up production via the DB at that point 🙂

seancorfield 2021-01-06T00:01:11.466100Z

With great power (REPL) comes great responsibility!

unbalanced 2021-01-06T00:43:17.466300Z

Hey everyone, this is a wonderful discussion! Glad to come back and find so many comments. I think that's a great point @didibus -- the argument for a remote Clojure REPL (if I'm understanding this correctly) -- and the argument for ssh are effectively the same argument. (also @seancorfield your notable insight is really helpful. Always good to see/hear how the pros like you and @hiredman do it!)

Cas Shun 2021-01-06T02:42:19.467500Z

Hello, I'm trying to understand why this happens:

user=> (var (symbol "meta"))
Syntax error (ClassCastException) compiling var at (REPL:1:1).
clojure.lang.PersistentList cannot be cast to clojure.lang.Symbol
user=> (eval `(var ~(symbol "meta")))
#'clojure.core/meta
Why is the first line invalid there, and is the second line a reasonable way to approach it?

2021-01-06T15:48:52.486Z

@auroraminor a warning about eval - it isn't done in the current lexical context

2021-01-06T15:49:04.486200Z

(that shouldn't matter for vars of course)

saitouena 2021-01-06T02:51:33.468500Z

var is a special form so var tries to convert a list (symbol "meta") (not a symbol meta as a result of the evaluation of (symbol "meta")) to a Var object and then explodes.

➕ 1
saitouena 2021-01-06T02:57:17.468800Z

you can use resolve to do it

user=> (resolve (symbol "meta"))
#'clojure.core/meta

saitouena 2021-01-06T02:57:31.469Z

https://clojuredocs.org/clojure.core/resolve

saitouena 2021-01-06T02:59:24.469200Z

https://clojure.org/reference/special_forms#var > Special forms have evaluation rules that differ from standard Clojure evaluation rules and are understood directly by the Clojure compiler.

suren 2021-01-06T05:16:41.470200Z

How to run a ring server while using deps.end instead of lein ?

seancorfield 2021-01-06T05:26:47.471400Z

@suren Take a look at https://github.com/seancorfield/usermanager-example which is a deps.edn project using Ring (and a few other libraries) to provide a fairly simple web app example.

suren 2021-01-06T21:54:18.023600Z

Wow, never thought starting a ring server without lein can be this complicated. Thanks anyway.

seancorfield 2021-01-06T21:56:59.023800Z

Huh? There's nothing complicated about just starting a Ring server. It's like two lines.

seancorfield 2021-01-06T21:57:47.024Z

But you want to learn about more than just Ring for building web apps and that combines a decent base of libraries -- and shows how you can start either Jetty or http-kit to run Ring.

seancorfield 2021-01-06T22:00:09.024200Z

(! 516)-> clj -Sdeps '{:deps {ring/ring {:mvn/version "RELEASE"}}}'
Downloading: ring/ring/maven-metadata.xml from clojars
Clojure 1.10.1
user=> (require '[ring.adapter.jetty :refer [run-jetty]])
2021-01-06 13:59:06.924:INFO::main: Logging initialized @18382ms to org.eclipse.jetty.util.log.StdErrLog
nil
user=> (defn app [req] {:status 200 :body "Hello, World!"})
#'user/app
user=> (run-jetty #'app {:port 8100 :join? false})
2021-01-06 13:59:44.580:INFO:oejs.Server:main: jetty-9.4.31.v20200723; built: 2020-07-23T17:57:36.812Z; git: 450ba27947e13e66baa8cd1ce7e85a4461cacc1d; jvm 15+36
2021-01-06 13:59:44.618:INFO:oejs.AbstractConnector:main: Started ServerConnector@658255aa{HTTP/1.1, (http/1.1)}{0.0.0.0:8100}
2021-01-06 13:59:44.619:INFO:oejs.Server:main: Started @56076ms
#object[org.eclipse.jetty.server.Server 0x26f46fa6 "Server@26f46fa6{STARTED}[9.4.31.v20200723]"]
user=> 
Ring/Jetty started with a minimal handler.

seancorfield 2021-01-06T22:00:23.024400Z

(! 627)-> curl localhost:8100
Hello, World!

suren 2021-01-11T08:05:31.047700Z

(run-jetty #'app {:port 8100 :join? false})
This is pretty simple. I got overwhelmed with the abstraction in the codebase that you pointed. Gonna bookmark this snippet. Thanks @seancorfield

seancorfield 2021-01-11T20:58:16.104Z

@suren If it helps, I've just updated the example code to a) remove all the conditional logic around http-kit (and instead include comments showing how to switch from Jetty to http-kit) and b) add more comments about the promise used to wait in -main and c) add more REPL-friendly comments and code for the context of using a socket REPL in the running program.

suren 2021-01-12T09:15:59.117200Z

@seancorfield thank you so much. The changes were helpful.

1
Schpaa 2021-01-06T11:15:37.474200Z

I guess everybody knew this, but I didn’t:

(let [input {:a 1}
        {:keys [a b] :or {b :default} :as m} input]
    ; Why isn't the (:default in) b carried forward in m in this case?
    ; The `:or` assigns defaults for some of the keys for this scope, 
    ; but doesn't alter the m, this has bitten me a couple of times now...
    [a b m]
    
    ; I have to assoc in order to bring the default further, or reimplement the 
    ; default in the next context:   
    (passing-further (assoc m :b :default))

Ed 2021-01-06T11:25:45.474400Z

To me the :or clause in the destructuring reads like "if the symbol b is unassigned give it the value :default" and says nothing about m ... m is the whole thing you're trying to destructure (basically input)

☝️ 1
Schpaa 2021-01-06T11:26:18.474600Z

Yes, that is totally reasonable

Schpaa 2021-01-06T11:26:47.474800Z

But while reading the code, my mind went on a completely different road, so to speak

Ed 2021-01-06T11:28:39.475100Z

the :keys bit is just shorthand for {b :b a :a} and could equally be {b :fish} and you'd still write the symbol not the keyword in the :or ... as in {b :default} not {fish :default}

☝️ 1
Schpaa 2021-01-06T11:29:26.475300Z

So I could put arbitrary assignments in the :or section?

Ed 2021-01-06T11:29:54.475700Z

I don't think so no

Schpaa 2021-01-06T11:30:27.475900Z

right…

Ed 2021-01-06T11:31:22.476100Z

you'd have to do

(let [input {:a 1}
        {:keys [a b c] :or {b :default c 1} :as m} input]
    [a b m c])

Ed 2021-01-06T11:32:14.476300Z

or

(let [input {:a 1}
        {:keys [a b] c :other :or {b :default c 1} :as m} input]
    [a b m c])

Schpaa 2021-01-06T11:34:46.476500Z

that last one does not make much sense to me

Schpaa 2021-01-06T11:37:17.476700Z

but I see that it allows for mentioning the c in the :or section

Ed 2021-01-06T11:37:18.476900Z

the :keys part is expanded out into {a :a b :b ... } so adding c :other is just adding a new local that's bound to the value of (:other input)

Schpaa 2021-01-06T11:37:45.477100Z

so the :other is just for keeping the fields even

Ed 2021-01-06T11:37:58.477300Z

you look at the code that actually gets generated with

(destructure '[{:keys [a b] c :other :or {b :default c 1} :as m} {:a 1}])

Ed 2021-01-06T11:41:00.477500Z

yeah ... the :other keyword is just something that's not expected to be there ... but I'd not expect that to be something you'd actually write in a program ... I just meant it as a response to your arbitrary assignments question .... if you wanted another local that was nothing to do with the input that's getting destructured, why would you include it in the destrucuring form rather than as a local in a let?

Ed 2021-01-06T11:41:26.477700Z

am I making sense?

Schpaa 2021-01-06T11:41:40.477900Z

absolutely

Ed 2021-01-06T11:41:51.478100Z

cool ... first time for everything 😉

Schpaa 2021-01-06T11:42:00.478300Z

thats cool

Ed 2021-01-06T11:43:38.478500Z

I found the destructure function useful when learning how that all worked ... and it's great that you can just call it from your own macros (if you need to) 😉

vemv 2021-01-06T15:36:52.485400Z

over the last few days I've played with java FileLocks and found that that a popular pattern, namely obtaining .lock from a FileChannel from a RandomAccessFile, simply doesn't work (on macOS, encrypted APFS). Other files will also be able to obtain the lock, while other process holds it, allowing race conditions to happen. Luckily this doesn't happen when using FileChannel/open directly. I think it's because RandomAccessFile is more oriented towards per-file-region locks, while FileChannel/open is more oriented towards whole-file locks. And my hypothesis is that per-region locks are less guaranteed to be implemented across all OSes/FSes. Does this sound familiar to anyone?

2021-01-06T15:45:06.485500Z

I have not used these methods before, but this StackOverflow Q&A might be relevant: https://stackoverflow.com/questions/56458266/java-nio-filelock-allows-other-process-to-write-to-a-locked-file

2021-01-06T15:45:21.485800Z

It appears that part of the Java docs for those methods say that locking behavior is OS-specific.

vemv 2021-01-06T15:51:24.486400Z

Yes, but file locking is certainly possible in my machine. Both java FileChannel/open, and traditional C flock (which I tried out via Ruby) effectively lock a file acros processes. It looks like in the JVM, some advanced features, if cannot be offered, degrade to "no locking at all" instead of to "a more coarse-grained kind of locking" Which would seem a pretty poor choice

2021-01-06T15:53:40.486600Z

Given the Java documentation for the FileLock class, all it promises is "advisory locking". If you want any more guarantees than that, it appears that those Java methods will not guarantee anything more for you. You can test and/or dig deeper into implementation details of how your JVM implements those methods, but without that work, you aren't going to get more promises from those methods than what is documented.

2021-01-06T15:54:59.486800Z

I can easily imagine that file locking mechanisms differ enough across different OS's that it would be at least difficult, and perhaps impossible, to promise the same behavior across all OS's.

2021-01-06T15:55:51.487Z

that is, to promise mandatory, versus merely advisory, file locking on all OS's

vemv 2021-01-06T15:57:47.487600Z

the mechanisms I described as "working" are advisory and I'm fine with it - I just want multiple JVMs under my control to honor a protrocol I think that RandomAccessFile can grant a "no-op" lock, not advisory I might even try reporting it as a bug although I wouldn't be optimistic :)

benny 2021-01-06T16:01:42.491600Z

what’s the benefit of using a namespaced keyword versus a qualified one? since i may not be using the exact correct terminology, an example:

(ns acme.events)

;...
::foo
;...

(ns acme.views
  (:require [acme.events :as events]))

[:a {:on-click #(submit ::events/foo)} "foo"]
vs
(ns acme.views)

[:a {:on-click #(submit :events/foo)} "foo"]

2021-01-06T16:02:49.491800Z

You are welcome to, but realize that they might just point you at that part of the Java documentation 🙂

2021-01-06T16:06:08.492400Z

::foo is a sugar for :current-namespace/foo and if you have aliased bar to b, ::b/foo is a sugar for :bar/foo

2021-01-06T16:06:40.492900Z

sometimes the extra information of the precise namespace the keyword was generated in is useful

2021-01-06T16:08:07.493600Z

or maybe a function expects a keyword matching its own namespace, to avoid collisions

clyfe 2021-01-06T16:21:51.001400Z

If it got a "/" in it, the keyword is "namespaced", the keyword namespace being what's before the "/" and the name what's after. "Keyword namespaces" and "clojure namespaces" have nothing to do with one another - but one can otherwise use matching names to imply provenance. I tend to use: • :foo/bar in entities (data that models a domain, the namespace identifies the entity and the name the attribute). • :actual.clj.nsp/bar when I want the keyword to give me a hint on what clojure namespace it's in relation with (where functions that make and process it are); also when I want to make sure I'm not gonna be in conflict with other libs / project / portions of colleagues code using the same keyword for a different purpose. • :plain otherwise

👍 2
telekid 2021-01-06T17:09:04.008500Z

IMO ::foo should be avoided for any keywords that will be used outside of code that you control, e.g. sent over the wire or used in a library config, since it limits your ability to make namespace changes to data. (We’re used to carefully considering the location of code that will be exposed as part of a public API, but we’re less accustomed to considering the location of values within our code as part of a public API.)

💡 1
clyfe 2021-01-06T17:11:15.009800Z

how does it limit it? one can use the fully qualified name externally, ie. :some.nsp/foo

2021-01-06T17:11:46.011Z

but that means a namespace refactoring inside your code means matching changes can be needed elsewhere

alexmiller 2021-01-06T17:11:51.011300Z

you would normally never have ::foo to send over a wire in the first place

💯 1
alexmiller 2021-01-06T17:12:02.011600Z

printing will never print an autoresolved keyword

2021-01-06T17:12:43.013100Z

I think they meant the value produced by resolving ::foo - since it includes the ns

alexmiller 2021-01-06T17:12:50.013400Z

ok

2021-01-06T17:13:06.014200Z

which means the implementation detail of your ns organization is now part of your data model

👍 1
telekid 2021-01-06T17:16:09.017Z

Sorry, I don’t mean literally sending ::foo – more that if you send :com.company.util.routing/something and then want to move that, now your code is forced to reference a keyword namespaced with a prefix that no longer reflects your code structure. I guess I’m just encouraging people to be particularly intentional about the selection of namespaces for values, and :: kind of lets you wave your hands over that decision.

👍 2
telekid 2021-01-06T17:17:36.019100Z

I’ve often wondered about what the implications of a separate namespace universe for “values” would be. But in lisp that distinction is really blurry, so I think that idea would probably fall apart pretty quickly. (Possibly shares some of the same tradeoffs from the lisp1/lisp2 argument.)

caumond 2021-01-06T17:19:46.020600Z

I use keyword in clojure namespace to enforce the requirement of the namespace:

(ns a)
(defmulti  my-multi :kw)
(ns b)
(defmethod a/my-multi ::foo [_] (println "here am I"))
(ns c)
(require '[a :refer [my-multi]] '[b :as b])
(my-multi {:kw ::b/foo})
In that way, I'm sure to be able to load namespace b in namespace a. This is useful for a kind of plugin system.

pavlosmelissinos 2021-01-06T17:56:35.021400Z

So this is mostly an issue with public data, right? Would it be prevented by always putting public API specs in a namespace that is either: 1. separate from where the code is, e.g. <org>.<app>.spec or 2. generic enough that it won't be changed, e.g. <org>.<app>.api? The reasoning here is to be able to replicate the user's experience, so renaming a namespace as described above should break the api on your end before it breaks on the user's end. I'm pretty sure I'm missing something though 🙂

telekid 2021-01-06T18:12:59.021600Z

Can changes be made atomically? If yes, then you’re right, this is probably a non-issue. If not, then you should be designing your data model with longevity in mind. “Public data” can’t be changed atomically. Neither can private data that crosses a non-transactional boundary. It turns out that most values cannot be reliably changed atomically - the only common exception that I can think of is library implementation details.

pavlosmelissinos 2021-01-06T18:26:23.022Z

I was mostly talking about detecting a breakage before it reaches the user. If you already know that e.g. renaming a map key breaks your api maybe you shouldn't do it or if you really don't like the current name, I can see two paths forward: 1. add the new name but keep the old one for compatibility reasons 2. depending on the extent of the breakage you might need to introduce a second API At least this is what I got out of https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/Spec_ulation.md...

telekid 2021-01-06T18:51:39.022500Z

Oh, I misunderstood you. Yeah, spec seems like a great way to draw a line in the sand between public and private values.