Fork me on GitHub
#clojure
<
2021-01-06
>
Cas Shun02:01:19

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?

saitouena02:01:33

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.

3
saitouena02:01:17

you can use resolve to do it

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

saitouena02:01:24

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.

noisesmith15:01:52

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

noisesmith15:01:04

(that shouldn't matter for vars of course)

suren05:01:41

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

seancorfield05:01:47

@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.

suren21:01:18

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

seancorfield21:01:59

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

seancorfield21:01:47

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.

seancorfield22:01:09

(! 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.

seancorfield22:01:23

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

suren08:01:31

(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 @U04V70XH6

seancorfield20:01:16

@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.

Schpaa11:01:37

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))

Ed11:01:45

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)

☝️ 3
Schpaa11:01:18

Yes, that is totally reasonable

Schpaa11:01:47

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

Ed11:01:39

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}

☝️ 3
Schpaa11:01:26

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

Ed11:01:54

I don't think so no

Ed11:01:22

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])

Ed11:01:14

or

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

Schpaa11:01:46

that last one does not make much sense to me

Schpaa11:01:17

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

Ed11:01:18

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)

Schpaa11:01:45

so the :other is just for keeping the fields even

Ed11:01:58

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}])

Ed11:01:00

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?

Ed11:01:26

am I making sense?

Schpaa11:01:40

absolutely

Ed11:01:51

cool ... first time for everything 😉

Schpaa11:01:00

thats cool

Ed11:01:38

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) 😉

vemv15:01:52

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?

andy.fingerhut15:01:21

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

vemv15:01:24

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

andy.fingerhut15:01:40

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.

andy.fingerhut15:01:59

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.

andy.fingerhut15:01:51

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

vemv15:01:47

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 :)

andy.fingerhut16:01:49

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

benny16:01:42

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"]

noisesmith16:01:08

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

noisesmith16:01:40

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

noisesmith16:01:07

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

clyfe16:01:51

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

👍 6
telekid17:01:04

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.)

💡 3
clyfe17:01:15

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

noisesmith17:01:46

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

pavlosmelissinos17:01:35

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 🙂

telekid18:01:59

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.

pavlosmelissinos18:01:23

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...

telekid18:01:39

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

Alex Miller (Clojure team)17:01:51

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

💯 3
Alex Miller (Clojure team)17:01:02

printing will never print an autoresolved keyword

noisesmith17:01:43

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

noisesmith17:01:06

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

👍 3
telekid17:01:09

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.

👍 6
telekid17:01:36

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.)

caumond17:01:46

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.