clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
2020-11-14T00:00:00.026900Z

I don't get it. Then why can't I load a namespace I create with create-ns the same way wtv ns does?

2020-11-14T00:00:22.027300Z

ns does more than just create the namsepace, it also registers it as something loaded

2020-11-14T00:00:48.028Z

Right... so now we are full circle no? Why doesn't create-ns does that as well?

2020-11-14T00:00:48.028100Z

so that the next require (without something like :reload) doesn't attempt to load it

2020-11-14T00:01:09.028600Z

because create-ns is a much lower level tool, iirc ns uses create-ns

2020-11-14T00:02:35.029Z

Hum, not from the code I look at

2020-11-14T00:03:06.029200Z

I mean, ns is a lot more complicated

2020-11-14T00:03:35.029700Z

I guess what confuses me here, is the concept of a namespace vs a lib and something that is loaded and not?

2020-11-14T00:03:55.030100Z

So I can create a namespace, but I can't ever load or require it?

2020-11-14T00:04:02.030300Z

That seems weird

2020-11-14T00:04:16.030600Z

loading and requiring need something to load or require from

2020-11-14T00:04:35.031100Z

if you are making something from scratch, you want create-ns or ns (you can also use in-ns but that's not useful)

2020-11-14T00:05:46.031700Z

I mean the only difference is that ns seem to add the name to loaded-libs.

2020-11-14T00:06:16.032Z

and changes your current namespace

2020-11-14T00:06:24.032300Z

which you probably don't want in the middle of a file

2020-11-14T00:06:38.032900Z

Ya, I guess the magic is inside in-ns but I think that's a special form in JAva

seancorfield 2020-11-14T00:06:57.033400Z

And ns will auto-refer in clojure.core

2020-11-14T00:07:33.034200Z

So I mean, semantically ns does a lot more. But the part where it creates a namespace seems to be doing it differently then when I call create-ns. That's what confuses me

seancorfield 2020-11-14T00:07:45.034600Z

(which is why if you do (in-ns 'foo.bar) in a fresh REPL, you suddenly don't have any core functions accessible!)

seancorfield 2020-11-14T00:08:16.035Z

@didibus What do you mean by "doing it differently"?

2020-11-14T00:08:54.035500Z

Using ns it creates a namespace that I can then require. But not so for create-ns

2020-11-14T00:09:23.035900Z

user=> (pprint (macroexpand-1 '(ns foo.bar)))
(do
 (clojure.core/in-ns 'foo.bar)
 (clojure.core/with-loading-context (clojure.core/refer 'clojure.core))
 (if
  (.equals 'foo.bar 'clojure.core)
  nil
  (do
   (clojure.core/dosync
    (clojure.core/commute
     @#'clojure.core/*loaded-libs*
     clojure.core/conj
     'foo.bar))
   nil)))

2020-11-14T00:09:31.036400Z

using require for something that doesn't have anything you can load is an off-label usage

seancorfield 2020-11-14T00:09:38.036700Z

ns adds the namespace to the list of loaded libs so require doesn't need to load it from disk.

2020-11-14T00:10:06.036900Z

Doing a successful ns on a namespace should, I believe, add it to *loaded-libs*, so that future calls to require on that namespace are a no-op unless you add something like :reload-all

seancorfield 2020-11-14T00:10:16.037400Z

user=> (ns quux.wibble)
nil
quux.wibble=> (in-ns 'user)
#object[clojure.lang.Namespace 0x3f22ce2a "user"]
user=> (require 'quux.wibble :reload)
Execution error (FileNotFoundException) at user/eval121530 (REPL:1).
Could not locate quux/wibble__init.class, quux/wibble.clj or quux/wibble.cljc on classpath.
user=> 

2020-11-14T00:10:24.037800Z

So I understand the implementation is why they differ. But I don't understand the conceptual reason or difference.

2020-11-14T00:10:42.038500Z

Like what is loaded when you use ns with no associated file or class

seancorfield 2020-11-14T00:10:46.038600Z

If you ask require to reload a namespace only created in memory, it fails, just like trying to require a namespace created by create-ns.

2020-11-14T00:11:45.039100Z

Exactly, and that's why I don't understand why create-ns doesn't also add it to loaded-libs

seancorfield 2020-11-14T00:11:59.039500Z

So ns = create-ns + add to loaded-libs + refer clojure.core + in-ns.

2020-11-14T00:12:06.039600Z

Maybe just an omission, or is there some conceptual reason

2020-11-14T00:12:52.040600Z

Hum, so I guess with that my question is, why doesn't create-ns adds it to loaded-lib ?

2020-11-14T00:13:08.041Z

I think because it is a lower-level function than the ns macro

seancorfield 2020-11-14T00:13:17.041200Z

Because it's a low-level primitive. Yeah, what he said.

2020-11-14T00:13:44.042100Z

Ok, but its weird. If it just created a namespace object I'd get it. But it also adds it to the global list of namespaces

2020-11-14T00:14:20.042500Z

That doesn't make it a higher-level function. It just means it does 2 things instead of 7

2020-11-14T00:14:41.043200Z

Haha, true true. I guess it just feels like not low level enough, and not high level enough

2020-11-14T00:14:50.043800Z

If create-ns did not add it to the global list of namespaces, then all-ns would not include such namespaces.

2020-11-14T00:15:03.044500Z

hardly anyone uses it in applications, I expect.

2020-11-14T00:15:05.044700Z

So would it be safe for me to add a create-ns namespace to loaded-lib ?

2020-11-14T00:15:31.045300Z

why? what's the utility here?

2020-11-14T00:15:45.045800Z

just to create aliases without calling alias ?

seancorfield 2020-11-14T00:16:43.046900Z

The namespace isn't going to contain anything so what would be the use in requiring it?

2020-11-14T00:18:10.048600Z

Hum, its a bit complicated. Basically, I have a data-model namespace where I create namespaces and alias them to use as my keys for my specs. Now in some other namespace, I have a fdef which I want to say this arg is of spec ::account/type but that is not a real namespace, but one I create with create-ns in data-model namespace. So my first thought was, ok, well I need to require :as the specs where I use them.

2020-11-14T00:18:54.049200Z

right, that's what alias is for (this is the only reason I have seen to care about namespaces that don't have code in them)

2020-11-14T00:20:08.049900Z

Well, ya I can use alias, it just seems a bit inconssistent

dpsutton 2020-11-14T00:23:20.050700Z

i think these are some of the shenanigans that i see hiredman point out as completely unnecessary hoops you have to jump through when you use namespace aliased keywords for your specs and i just agree more and more as time goes on

2020-11-14T00:24:01.051400Z

also, nothing stops you from doing (create-ns :account) and then using :account/type - it doesn't need aliasing to work

dpsutton 2020-11-14T00:24:09.051700Z

if you just did :account/type doesn't this just go away?

2020-11-14T00:24:29.052300Z

if you have more than one thing that would shorten to :account in one codebase you are doing something weird anyway

seancorfield 2020-11-14T00:24:30.052400Z

You don't need to create a namespace for :account/type

💯 2
2020-11-14T00:24:39.052600Z

@dpsutton exactly

2020-11-14T00:24:55.052800Z

Ya, its definitely missing something. But I find none is ideal. If you go with :foo/bar instead of ::foo/bar, then you lose compile error for non existent namespace

2020-11-14T00:25:40.053300Z

if you are actually using the spec you will error on one side or the other for the spec not being found...

2020-11-14T00:26:20.053900Z

I feel spec should have used interned symbols. And s/keys shouldn't have done its weird magic of like the key is the spec and the key. Should have been like: (s/keys :req {spec :foo/bar})

2020-11-14T00:27:22.055300Z

That creates conflicts

seancorfield 2020-11-14T00:27:30.055700Z

As Alex said, there's probably going to be something built into Clojure 1.11 to make this more tractable -- they just haven't figure out the right thing for it yet. And I suspect the pain you're going through trying to "solve" this today @didibus reflects why they're still trying to figure it out 🙂

1
2020-11-14T23:31:58.094Z

Is there a want for symbol aliases that don't require reified namespaces as well? Or just for keyword aliasing?

alexmiller 2020-11-15T01:55:08.094300Z

an excellent question we have considered. I don't have a use case for lightweight symbol aliases right now but would be curious to see one if you find one. symbols don't have auto-aliasing so that's one context that doesn't exist.

2020-11-16T23:28:54.121300Z

I don't have one either, was just curious.

2020-11-16T23:30:40.121500Z

By the way, I don't know if it helps. But where I've landed with my experimentation is something very similar to how symbols and namespaces work. I quickly details it here: https://clojureverse.org/t/spec-ing-entities-and-how-to-organize-the-specs-and-our-entity-keys-a-new-approach/6817/8?u=didibus

alexmiller 2020-11-17T00:23:31.123500Z

Bleh

alexmiller 2020-11-17T00:24:10.124200Z

Stateful non-lexical scope is gross

2020-11-17T00:24:54.124400Z

Is it not lexical? I think it is

alexmiller 2020-11-17T00:26:03.125300Z

If you look at just in-scope by itself what does it mean?

alexmiller 2020-11-17T00:26:12.125700Z

You have no way to know

alexmiller 2020-11-17T00:26:38.126200Z

Or I guess defalias by itself

2020-11-17T00:26:56.126400Z

Well, you know that every line of code after it which calls defalias will use the in-scope namespace

2020-11-17T00:27:05.126700Z

Same as in-ns

alexmiller 2020-11-17T00:27:28.127200Z

Bleh

alexmiller 2020-11-17T00:27:35.127500Z

Hate it :)

2020-11-17T00:27:39.127700Z

The only "tricky" part, similar to in-ns, is that the scope isn't put inside of a delimiter block, but is flat

2020-11-17T00:28:13.127900Z

Haha, well I agree with you a little. But the alternative was:

(with-scope [<http://com.app|com.app> :alias [user]]
  (defn ...)
  (defn ...)
  (s/def ...)
  (s/fdef)
  (def)
)

2020-11-17T00:29:22.128200Z

Which completely messed up clj-kondo. And felt less Clojury. Kind of gave me vibes of Java Classes actually (which is interesting, since its really the same problem of Entity scope that OOP has that I have here as well. I'll see how it goes in practice, but for now it works pretty much the same as symbols and namespaces, so I've found it quite intuitive. Like I'm already used to symbols having an implicit namespace that is defined by some preceding call to ns or in-ns. So this felt normal.

2020-11-17T00:36:48.129Z

There's a chance I drop the in-scope as well, and just accept the fact I'm going to be using defalias like:

(defalias 'user 'com.org.app.user)
(defalias 'transact 'com.org.app.transact)
(defalias 'account 'com.org.app.account)
(defalias 'cart 'com.org.app.cart)
And just copy/paste this everywhere. The repetition annoys me, but maybe the clarity is worth it, not sure yet.

2020-11-17T01:14:26.134Z

Well, ok, had to rethink this again 😛 If Alex Miller don't like it, something must not sit right with it lol. And I realized the only reason for my in-scope was that I was currently sprinkling the defalias call throughout my data-model namespace, above their corresponding s/keys for each. But I don't need to do that. Then I can scope all alias in a single block, put it at the top (and ideally ns could be extended to have an :alias option as well, making it even better.

2020-11-17T01:20:00.134200Z

(ns com.org.app.data-model ...)

(alias '[<http://com.org.app|com.org.app> :as app :refer [user account transact cart]]
       '[com.org.other-app :as other-app :refer [user] :rename {user other-user}])

::user/name
;; =&gt; :com.org.app.user/name

::app/user
;; =&gt; :<http://com.org.app/user|com.org.app/user>

::other-user/name
;; =&gt; :com.org.other-app.user/name

2020-11-17T01:21:47.134400Z

Not sure about the DSL here. Maybe it doesn't have to shoehorn :refer and :as and :rename, could use some other DSL. But I think with this one, I'll be able to tell clj-kondo to lint it as require and it should work which is nice.

alexmiller 2020-11-17T01:58:52.135100Z

I don't know what problem you're trying to solve at this point

alexmiller 2020-11-17T01:59:31.135300Z

why do want renaming and referring? just seems like a lot of stuff to make it as hard as possible to understand what kw you're actually using

2020-11-17T02:07:33.136500Z

Say I have a map which models a Person's Name:

{:name "John Doe"}
And so I have a spec for it (s/def ::name (s/keys :req [::name])) And now some dependency of mine also has a spec for a map that models a name, but the map is different:
{:first-name "John"
 :last-name "Doe"}
And also have a spec: (s/def ::name (s/keys :req [::first-name ::last-name])) Now some service uses both of these. That service will s/fdef the functions which uses those maps and those maps will also be persisted forever in some DB. So there are two problems with the above, if the namespace in which the spec are defined changes, all my fdefs are broken, and all my keys are broken. But also if I don't use a namespace on my keys, the specs and map keys now conflict. Also, with the first example, the spec for the key and the map itself also conflicts. So that's the scenario. It means I need a way to namespace my specs and my keys that is independent from the namespace they are in, and also that avoids conflicts. But I also don't want to fully be typing giant namespaces everywhere, since that's both annoying and ugly, but also prone to hard to find typos.

2020-11-17T02:12:24.136800Z

And imagine my function that uses those is this:

(ns foo.bar
  (:require [com.org.my-app.name :as name]
            [com.org.some-dep.name :as other-name]))

(defn name-&gt;other-type-of-name
  [name]
  {::other-name/name (str (::name/first-name) "." (::name/last-name))})

(s/fdef name-&gt;other-type-of-name
  :args (s/cat :name ::name/name)
  :ret ::other-name/name)

2020-11-17T02:14:07.137Z

So here I'm using the normal ::. It means that each entity must be in their own namespace so I can require each one, so imagine I also had a map called group, and account, and all that, and I didn't want their spec all in separate files. It also means that my specs and keys can be broken inadvertently by a refactor of those namespaces, so if the specs ever move to some other namespace, my code is broken.

alexmiller 2020-11-17T02:19:03.137500Z

a) the only thing that will work in the spec registry is sufficiently qualified ("long") namespaces b) what you want to type in your code is something short (an alias, "short" name) c) existing aliases give you exactly this feature, with the constraint that the aliased qualifier must be an actual namespace d) the thing we're going to add is the ability to do c but without that constraint (actually api still in flux)

alexmiller 2020-11-17T02:19:21.137700Z

when you say "refactoring", I hear "breaking stuff"

alexmiller 2020-11-17T02:19:42.138Z

if you don't like stuff broken, don't break stuff

2020-11-17T02:21:19.138200Z

Well, since the spec registry is global, and keys outlive an app, I'd rather not couple them to some particular code namespace.

2020-11-17T02:24:30.138400Z

So with c without the constraint, I would do:

(ns foo.bar
  (:require [com.org.my-app.specs]
            [com.org.some-dep.specs]))

(alias 'name 'com.org.my-app.name)
(alias 'other-name 'com.org.some-dep.name)

(defn name-&gt;other-type-of-name
  [name]
  {::other-name/name (str (::name/first-name) "." (::name/last-name))})

(s/fdef name-&gt;other-type-of-name
  :args (s/cat :name ::name/name)
  :ret ::other-name/name)
Which works fine. Except if my-app has 28 entity specs, than I need to do:
(alias 'name 'com.org.my-app.name)
(alias 'user 'com.org.my-app.user)
(alias 'car 'com.org.my-app.car)
(alias 'etc  'com.org.my-app.etc)
Which is not that bad, but :refer was a way to shortcut the repetition here. The other thing with :as and :refer, is I guess what I mentioned with (s/def ::name (s/keys :req [::name])). What namespace should the map belong too? And what namespace should the key of the map belong too? Ideally it would be the map is :my-app/name and the key inside it is: :<http://my-app.name/name|my-app.name/name>. But now again, the short alias makes this difficult, because this does not work: ::<http://app.name/name|app.name/name> is an error

2020-11-17T02:32:52.139100Z

So that last issue is why when I alias, I'd like to be able to refer to the "parent namespace" like <http://com.org|com.org>.my-app as well as the child namespaces like: com.org.my-app.name. I can still do it with c like so:

(alias 'my-app 'com.org.my-app)
(alias 'name 'com.org.my-app.name)
(alias 'user 'com.org.my-app.user)
(alias 'car 'com.org.my-app.car)
(alias 'etc  'com.org.my-app.etc)
But again, I do this all the time, so I thought shortening this would be good.

2020-11-17T02:34:16.139300Z

It ends up very similar to Java honestly, with how it has a package.class/field. Basically, in practice, I've felt like I needed this scheme for my specs and my keys as well: context.entity/key

2020-11-17T02:35:56.139500Z

So I think of it as:

(alias '[some-context :as context :refer [entity1 entity2 entity3]])
And then I can define the keys with ::context.entity2/key

alexmiller 2020-11-17T02:39:16.139700Z

it seems like it's impossible at that point to have any idea what that actual keyword is

alexmiller 2020-11-17T02:39:49.139900Z

(obviously it's possible, but it would require walking through several distinct mappings to get there)

alexmiller 2020-11-17T02:41:04.140100Z

I have not seen any other cases where someone was taking aliases to this degree of phased construction

2020-11-17T02:42:03.140300Z

Well, most people stopped using them, and instead hand type: :my-app.entity/key

2020-11-17T02:42:33.140500Z

I see that a lot: :cool-app.user/name

2020-11-17T02:42:57.140700Z

And :cool-app/user or sometimes: :cool-app.user/user depending on convention, though latter means you can't have the map and the key inside be named the same like in my ::name map with key ::name

alexmiller 2020-11-17T02:46:00.141200Z

I generally see people using a relatively small number of qualifiers for specs. it could be that that's because it's hard otherwise, but I did actually survey a large number of uses of alias / create-ns / and similar things on github to see what was out in the wild.

seancorfield 2020-11-17T02:46:57.141400Z

As far as I'm concerned, anything in a library that should be combined with other code, should use spec names that match actual namespaces, or at least fully-qualified names that "match" similar namespaces in the library. The whole point of qualified keywords in specs is that they shouldn't conflict with other code.

2020-11-17T02:47:48.141600Z

I've done the same, because it was too hard personally. You either go: :app.entity/key or even lazier: :app/key and then your keys are called: :app/entity-key. And you forget trying to have a full URI like <http://com.org|com.org> and hope there won't be app name clashes.

seancorfield 2020-11-17T02:47:51.141800Z

You can easily use short names just within your own code (modulo not conflicting with obvious library names -- but if libraries as using properly qualified names, that won't be an issue).

seancorfield 2020-11-17T02:48:30.142100Z

I think you're just making life harder for yourself (and you know my opinion on this already I suspect 🙂 )

2020-11-17T02:49:23.142400Z

I mean, as soon as I say :: people respond with don't use ::, I don't use it, etc. There's a reason for that

2020-11-17T02:50:02.142600Z

It is a problem when speccing your domain model though. Data that will outlive your code over time.

seancorfield 2020-11-17T02:50:03.142800Z

<http://com.org|com.org> is a straw man.

2020-11-17T02:50:38.143Z

Haha, I mean replace org with the name of your org: com.worldsingles.user/id

seancorfield 2020-11-17T02:50:43.143200Z

A few people say "don't use ::"

alexmiller 2020-11-17T02:52:14.143400Z

I mean for me, I just fully qualified names in specs most of the time. I can't remember anything and then when I look at the code next, it's explicit. most code using the data is transforming it (not constructing it) and then I use :foo.bar/keys in destructuring or whatever and that covers a lot of places where I'm actually typing those namespaces.

2020-11-17T02:53:54.143600Z

The thing with :: is the same as dynamic vars. People don't realize that your data escapes the scope of your namespace. I've had multiple devs on my team make this mistake. They use :: in their specs, and thus their keys, and then don't realize now when you go to the DB, or return to the client, that namespace is the key's namespace as well. And maybe the spec started in: com.org.app.api. And in the future there is a service rewrite, in a new package of a different app name, or api was a bad place to put the spec cause now there are many APIs using it, and you need to move it out to prevent a circular dependency, but now it changes the key and breaks your clients (has happened to us)

alexmiller 2020-11-17T02:55:16.143800Z

I don't think that's a problem I can fix :)

seancorfield 2020-11-17T02:55:16.144Z

Are you using Datomic?

2020-11-17T02:56:01.144200Z

@alexmiller When you say you use: :foo.bar/keys what do you make foo and bar equal too? Do you purposely try to find a shorter name then your application namespace? Is bar ever the entity name? Or do you do :foo.bar/entity-key ?

alexmiller 2020-11-17T02:56:35.144400Z

"Do you purposely try to find a shorter name" - generally, no

seancorfield 2020-11-17T02:57:34.144600Z

Qualification of keywords is about how unique you need the names to be.

alexmiller 2020-11-17T02:57:36.144800Z

if relevant, I think entity would be part of the qualifier

2020-11-17T02:59:44.145Z

Not using Datomic no. Even without a DB though:

(ns com.my-company.my-service.api1)

(s/def ::user (s/keys :req [::id ::name ::email]))

(defn api1
  [input-request]
  {::id "123" ::name "John" ::email "<mailto:john@gmail.com|john@gmail.com>"})
It starts like this, and then another API starts using the ::user map, so the spec is moved into a common com.my-company.my-service.specs namespace, but the client is broken now, because the namespace of the keys in the user map were changed by accident.

seancorfield 2020-11-17T03:00:13.145200Z

Your refactoring was wrong then.

seancorfield 2020-11-17T03:00:28.145400Z

As Alex says, if you don't want things to break, stop breaking them.

seancorfield 2020-11-17T03:00:40.145600Z

The database doesn't care about namespace qualifiers.

2020-11-17T03:01:11.145800Z

Yes it was, but the language kinds of make you prone to it. Like at first :: pretends to be this nice convenience, but it turns out that its really inconvenient in a non toy example.

2020-11-17T03:02:30.146Z

So you'd be much better doing:

(ns com.my-company.my-service.api1)

(s/def ::user (s/keys :req [::id ::name ::email]))

(defn api1
  [input-request]
  {:com.my-company.my-service/id "123"
   :com.my-company.my-service/name "John"
   :com.my-company.my-service/email "<mailto:john@gmail.com|john@gmail.com>"})
Except now you wish there was a more convenient syntax for these loooooong namespaces on your keys you have everywhere

2020-11-17T03:03:11.146300Z

Those are real ugly in destructuring code as well

alexmiller 2020-11-17T03:03:18.146500Z

like #:com.my-company.my-service{:id "123" :name "John" :email "<mailto:john@gmail.com|john@gmail.com>"} ?

alexmiller 2020-11-17T03:03:37.146700Z

and like (let [:com.my-company.my-service/keys [id name email] ...)

2020-11-17T03:04:12.146900Z

Ya, in a toy example as well, until your payload returns a map of user + item keys mixed in, now you can't map alias both of them.

alexmiller 2020-11-17T03:04:18.147100Z

yes you can

2020-11-17T03:04:24.147300Z

Can you?

seancorfield 2020-11-17T03:04:27.147500Z

Yes.

alexmiller 2020-11-17T03:04:34.147900Z

use 2 :...keys

alexmiller 2020-11-17T03:05:01.148300Z

and both of those also support ::alias/ syntax too

seancorfield 2020-11-17T03:05:06.148500Z

I have no problem with :: (just for the record).

alexmiller 2020-11-17T03:05:21.148700Z

(with the pesky constraint that the alias has to be a real namespace)

2020-11-17T03:05:24.148900Z

Hum, ok I didn't know that. So I just prepend a number to it?

seancorfield 2020-11-17T03:05:27.149100Z

I'm perfectly happy to use ::alias/key for the shorter name.

alexmiller 2020-11-17T03:05:50.149300Z

no, I was just you could use two different :http://some.name/keys in the same destructuring map

seancorfield 2020-11-17T03:05:55.149500Z

{::foo/keys [a b c] ::bar/keys [b c d]}

alexmiller 2020-11-17T03:06:00.149700Z

^^

alexmiller 2020-11-17T03:06:27.149900Z

for a map {::foo/a ... ::foo/b ... ::foo/c ... ::bar/b ... ::bar/c ... ::bar/d ...}

2020-11-17T03:07:27.150200Z

Well, ok, the way I use namespaces on key is like so. My entity specs are keyed on: :unique-namespace/entity and my entity fields are keyed: :unique-namespace.entity/key. So at the end of the day, I just need a way to make unique-namespace shorter, because to make it unique I make it really long as I follow the format: <http://com.org.app|com.org.app>

2020-11-17T03:08:07.150400Z

That's my problem I'm looking a solution for

alexmiller 2020-11-17T03:10:20.150600Z

I think aliases are the solution to most of that

2020-11-17T03:10:40.150800Z

So imagine I have:

(s/def :com.my-company.my-service/user
  (s/keys :req [:com.my-company.my-service.user/id]
                :com.my-company.my-service.user/name
                :com.my-company.my-service.user/address
                :com.my-company.my-service.user/email
                :com.my-company.my-service.user/dob]))
How would I manage this in a shorter syntax way?

seancorfield 2020-11-17T03:10:49.151Z

Aliases.

alexmiller 2020-11-17T03:11:59.151200Z

(alias 's 'com.my-company.my-service)
(alias 'u 'com.my-company.my-service.user)
(s/def ::s/user
  (s/keys :req [::u/id]
                ::u/name
                ::u/address
                ::u/email
                ::u/dob]))

👍 1
alexmiller 2020-11-17T03:12:32.151400Z

although tbh, I'm perfectly fine with the original :)

2020-11-17T03:13:09.151600Z

So like this?

(alias 'my-service 'com.my-company.my-service)
(alias 'user 'com.my-company.my-service.user)

(s/def ::my-service/user
  (s/keys :req [::user/id]
                ::user/name
                ::user/address
                ::user/email
                ::user/dob]))

alexmiller 2020-11-17T03:13:33.151800Z

as I said, yes

alexmiller 2020-11-17T03:14:30.152100Z

g'night all, stepping away

2020-11-17T03:14:48.152300Z

Ya, this is fine (assuming if they are not real namespaces for those alias will still work). Its just when I started using it in my app like that, the top of my namespaces started looking like Java import statements, they became huge, like 20 lines long, because I have big namespaces that use a lot of entities, so I was just looking for a shorter way to define those aliases. Maybe I've become allergic to verbosity since I moved away from Java 😛, but it was a sore point on my eyes.

2020-11-17T03:15:47.152600Z

Thanks Alex, much much appreciated. Have a good night.

2020-11-17T03:40:12.153Z

And thanks Sean as well for the input

1
2020-11-14T00:28:00.056400Z

Its super likely that some package depends on a library that has an :account/type spec and that you also have an :account/type spec

2020-11-14T00:29:01.057400Z

Actually is the case for me. This lib has a different way it models accounts from the main service. In both I want an account spec for how they model it.

2020-11-14T00:31:22.057600Z

I'm looking forward to it. And ya, all my attempts have had issues.

2020-11-14T00:33:57.057800Z

Though this last one seems the best yet. I was surprised that I can't require... but using alias will do

alexmiller 2020-11-14T02:48:44.064Z

Well the “want” is easy - aliases that don’t require reified namespaces. Lots of interesting choices in the impl

javahippie 2020-11-14T12:03:33.068700Z

I believe I am having some issues with Singletons from a Java library in the REPL. The affected project is a leiningen project, I am using Cider from within Emacs. Im working with Testcontainers, and the library uses a singleton class ResourceReaper, which can be accessed statically. Every container instance which is created registers itself in that ResourceReaper. This reaper provides a method to stop and remove all container instances. If the library is used from a Clojure REPL, a lot of instances might be created and I would like to clean them up with a call. When I call the instance method of the Java library (see code block), I seem to get a new instance of ResourceReaper. It does not contain any registered containers. Might there be multiple Java instances in the background that I don’t know about?

javahippie 2020-11-16T08:10:14.106800Z

That’s a good point, thanks! I guess it is not too far of to assume, that there might be multiple classloaders at work when connecting Cider to a REPL… :thinking_face:

javahippie 2020-11-14T12:05:54.069200Z

This is how I try to obtain the instance from the REPL, and perform a lookup of the (private) field registeredContainers (for debugging only, atm)

emccue 2020-11-14T16:53:46.071200Z

@javahippie If you call instance multiple times you should get the same object every time

emccue 2020-11-14T16:54:11.071900Z

the only exception to this would be if some other method on ResourceReaper cleared out the static field

emccue 2020-11-14T16:54:59.072600Z

in which case, and you should read the docs to confirm, I would expect there to be some cleanup logic before it loses the reference to the instance

emccue 2020-11-14T16:56:59.073200Z

maximum cringe at the death note references in the code though

emccue 2020-11-14T16:57:13.073700Z

it doesn't even make that much sense

javahippie 2020-11-14T17:22:43.077500Z

Thanks! From the docs (and checking back with the maintainers) this instance should not be cleaned out from anywhere, except when the JVM is shutdown. If the tracked instances were removed, the Docker containers belonging to those instances would be gone, too, but they are still there. But if this is not a “known” behavior for Java Singletons in a REPL, then it’s not really a fitting question for this channel 😉 I will try and debug into the Java code, maybe this clears something up.

borkdude 2020-11-14T17:25:36.078500Z

Why exactly does prepl need read+string? Was read+string invented for prepl? I have seen two things using prepl now and neither use the raw string I believe, but I'm curious about the reason it exists

seancorfield 2020-11-14T18:11:40.080400Z

@borkdude As I recall, yes, it was added for prepl support and it was a nice, clean way to get back both an expression that was read in and the remaining string that is leftover, so you can iterate over the data easily.

seancorfield 2020-11-14T18:16:25.080900Z

(which is particularly useful when the read fails after a certain character I think)

borkdude 2020-11-14T18:34:22.081400Z

@seancorfield read+string gives back the read string, not the remaining string?

user=&gt; (with-in-str "#(foo %) [1 2 3]" (read+string))
[(fn* [p1__139#] (foo p1__139#)) "#(foo %)"]

2020-11-14T19:03:02.083Z

Just guessing, but perhaps to enable features that might give error messages based on the original expression, and/or save source code for later viewing?

borkdude 2020-11-14T19:08:21.084800Z

(added a feature to edamame to have the original source as metadata on each expression:

user=&gt; (map meta (m/parse-string "[x y z]" {:source true}))
({:source "x", :row 1, :col 2, :end-row 1, :end-col 3} {:source "y", :row 1, :col 4, :end-row 1, :end-col 5} {:source "z", :row 1, :col 6, :end-row 1, :end-col 7})
)

seancorfield 2020-11-14T19:25:50.085600Z

Oh right, yeah, it's been a while since I looked at it. That's right, because they want the string version of the form that was read in, because that's what prepl returns, right?

seancorfield 2020-11-14T19:26:32.086400Z

I remember looking at this right when it was released but haven't done anything with it since... tool builders still say prepl doesn't really work well enough for them 😞

2020-11-14T19:53:03.090500Z

I used prepl once. Doing some adhoc metrics collecting at work, I would connect to our normal-ish repo then invoke prepl to turn the normal reply into a prepl and then speak prepl maps back and forth evaluating forms to collect numbers. It seemed ok, but I definitely wasn't using all the bits and bobs

borkdude 2020-11-14T20:35:24.091300Z

I could see the output from the io-prepl being useful to record repl sessions and print them in a readable (e.g. markdown) format:

borkdude 2020-11-14T20:36:12.091500Z

./bb -e '(clojure.core.server/io-prepl)' &lt;&lt;&lt; '(defn foo []) (foo)'
{:tag :ret, :val "#'user/foo", :ns "user", :ms 2, :form "(defn foo [])"}
{:tag :ret, :val "nil", :ns "user", :ms 0, :form "(foo)"}
Surely I'm going to use this output format for writing some tests

the2bears 2020-11-14T22:14:46.092100Z

Just a thought, but a singleton in Java might not actually be a singleton. It's been awhile since I spent time with class loaders, but if I recall correctly you might have a case of "peer" ClassLoaders, same parent. Each of those can instantiate the ResourceReaper class and the two will be considered different. Consider this as a class being a singleton only with respect to its ClassLoader. Usually this doesn't come up, as the loading hierarchy works this out, but for example in the case of OSGi things are done to break the hierarchy somewhat.