Fork me on GitHub
#clojure
<
2021-07-24
>
phronmophobic00:07:09

> having a GC per "process" is just such a cute hack I believe that's what erlang does. Although, it should be easier for erlang since there is strong isolation between erlang processes

deleted00:07:48

it's funny you should say! beam is erlang's vm πŸ˜„

phronmophobic00:07:31

😳 I guess I only read a screen full of messages and should have scrolled up more

phronmophobic00:07:21

but yea, there's lots of really interesting things about erlang

didibus01:07:42

It gives low GC pause, but then it also gives slower application performance since communication between the processes is now more expensive

emccue03:07:16

i guess i should take a look at elixir

emccue03:07:37

(or clojerl, but like - I'd prefer to understand the platform first before making that dive)

phronmophobic04:07:19

if nothing else, I think the erlang paper is worth reading and rereading, http://erlang.org/download/armstrong_thesis_2003.pdf

πŸ’― 3
tengstrand15:07:27

I try to create my own prompt in a program by using the https://clojuredocs.org/clojure.core/read-line function. When I run the code and the cursor is waiting for input, I can type β€œnormal” characters, but when I press e.g. β€œarrow up” it prints out β€œ^[[A”. Are there any alternatives to the read-line function, or any workaround for this? Maybe listening for single key strokes could be a way, because then I could implement something intelligent when the user presses arrow up or down. In https://stackoverflow.com/Questions/1066318/how-to-read-a-single-char-from-the-console-in-java-as-the-user-types-it thread they suggest to put the console in β€œraw mode” but unfortunately there are no built-in platform-independent support for that.

borkdude15:07:59

@tengstrand in Java this is almost impossible, unless you use something like jline

tengstrand15:07:36

Okay, thanks!

popeye15:07:52

I have scenario where there are 2 maps ,

{:key1  {:key2 {:key3  {:key4 {"a" "a"}}}}}
{:key1  {:key2 {:key3  {:key4 {"b" "b"}}}}}
If I do merge on both I am getting {:key1 {:key2 {:key3 {:key4 {"b" "b"}}}}} how can I get both keys as {:key1 {:key2 {:key3 {:key4 {"a" "a" "b" "b"}}}}}

dpsutton16:07:23

You want a deep-merge. A simple way to get one version of this is (merge-with merge map1 map2)

dpsutton16:07:17

i'm not actually quite sure how this works but

(letfn [(f [m1 m2] (merge-with f m1 m2))]
  (f {:key1  {:key2 {:key3  {:key4 {"a" "a"}}}}}
     {:key1  {:key2 {:key3  {:key4 {"b" "b"}}}}}))
{:key1 {:key2 {:key3 {:key4 {"a" "a", "b" "b"}}}}}

πŸ‘ 3
jsn17:07:33

@dpsutton it works by not using f when there's no collision (which happens in the end of your example, at {"a" "a"} / {"b" "b"}; that's why it fails for e.g. {"b" "a"} / {"b" "b"}

dpsutton17:07:01

good point. i knew it was missing a base case but kinda thought it would fail before then

lilactown17:07:19

what's the state of the art w.r.t. component/integrant/mount?

Ben Sless17:07:24

don't forget clip I've come to the opinion it's best to just use component + aero, unless you want to experiment with something exotic and build your entire application with pipelines or as a state machine

dharrigan17:07:49

I concur with Ben. Clip (with built-in aero support) is a great addition.

Ben Sless17:07:28

I find the main "complaint" which is the rationale for clip a non-issue. You can pretty trivially write a reader which encodes component systems as data. Clip needs a metacircular evaluator to work

Ben Sless17:07:24

Meanwhile, you can write a multimethod which together with aero obviates the need for clip

{:system
    {:db :make-db
     :server :make-server
     :app {:fn :make-app
           :using {:database :db :server :server}}}
    :config
    {:db {:url "foo.bar"}
     :sever {:port 8888}
     :app {:id 3}}}

emccue18:07:17

Or, just do all the bootstrapping yourself

emccue18:07:39

I consider that an option maybe more than others

Ben Sless18:07:23

It's always a viable option. I'd probably do it on a personal project but on a team project I would default to component

Ben Sless18:07:38

With one extra artificial constraint - never pass the component's content as an argument to a function. No need to start swearing at 3am one year in the future

Ben Sless18:07:05

But it might be an overly-opinionated position

lilactown18:07:34

cool. doesn't sound like there's much outside of what I already knew about

lilactown18:07:59

curious why people keep trying to do the whole "system as data" thing a la integrant/clip over something more programmable

Ben Sless18:07:51

"It's just data"

seancorfield18:07:23

We've used Component for a long time and now the protocols are metadata-extensible we like it a lot more πŸ™‚

p-himik18:07:45

Easier to reason about immediately. Compare {:a 0, :b 1} to (into {} (map vector [:a :b] (range))). Being constrained to "just data" makes you think in a specific way - one that often results in a nicer architecture, IMO.

πŸ‘ 6
seancorfield18:07:54

We started out without using anything and migrated to Component slowly over time (contrary to some claims that it is "all or nothing" -- it isn't).

lilactown18:07:31

I've the most experience with component and mount

seancorfield18:07:28

I've kept looking at alternatives. I really don't like Mount. I think Integrant is interesting but having seven lifecycle extension points seems over the top, and the multimethod approach means you sometimes have to hunt around to find all the relevant parts of how a particular dependency works -- I don't find it very intuitive, compared to Component, and I'm not terribly obsessed with "Data all the Things" so that aspect doesn't sway me much.

seancorfield18:07:51

I haven't looked at Clip yet...

vemv18:07:32

I've used almost exclusively Component. I like it in terms of simplicity but https://github.com/weavejester/suspendable looks like its missing piece, which presumably is built-in into Integrant For example, what should a DB-backed component do on reset? Wipe its data? That would be unfriendly for daily development, but in a way it also makes sense and will be occasionally desired, so there's no universal answer That's where Integrant's granularity would seem an improvement.

borkdude18:07:31

There was a podcast about mount on the clojurescript podcast. I've never used it. There was also one episode about component. We use component at work and one project has integrant, but personally I don't really have a preference.

seancorfield18:07:40

Suspendable sounds interesting but the problem it solves is not one that I actually have with my workflow.

seancorfield18:07:27

I need to make an effort to listen to the ClojureScript podcast about Mount.

borkdude18:07:51

As the author said on the podcast, it has some "loud haters" in the community, but I think he explained quite well some of the problems it addresses differently than the others.

vemv18:07:20

Mount has some fundamental limitations, lack of reified system means you can't test in parallel for example Of course anyone is free to trade off that limitation for some ergonomics etc

πŸ‘† 1
lilactown18:07:19

component also has that problem yeah?

borkdude18:07:32

He addressed that in the podcast by saying that he actually doesn't want multiple systems in parallel for testing, he wanted a separate process for testing. Ah well.

borkdude18:07:52

No, component can have multiple systems. The system is one value you can pass around.

borkdude18:07:09

But to be honest, in all the years of using it, I've only ever had one system at a time.

borkdude18:07:51

I haven't written a lot of systems where I mock all the components, etc. I tend to test the "real" system if I can

vemv18:07:43

> He addressed that in the podcast by saying that he actually doesn't want multiple systems in parallel for testing, he wanted a separate process for testing. Ah well. yeah as hinted anyone is free to make tradeoffs. But the alternatives are simply superior in this regard, they have a superset of features regardless of what you choose to use In this case you don't exactly get to call critics 'loud haters'

Ben Sless18:07:37

If you work in a monorepo you can even bring up multiple systems of multiple services in a single process

πŸ‘€ 3
Ben Sless18:07:00

And the ergonomics of mount fall on their face about a week after you build the system Implicit side effects, impure tests which redef everything, meh, the biggest argument against mount is someone else's code

πŸ™‚ 3
Ben Sless18:07:17

Most of use don't work alone. When you factor in collaboration and programming over time, component's explicitness is suddenly very welcome

βž• 3
borkdude18:07:15

And now with :extend-via-metadata arguably component has gotten "easier" too

borkdude18:07:33

(although I haven't really used it in that way yet)

Ben Sless19:07:02

I've been toying with the idea of a generic component. It has two explicit fields, a state and a function. Its constructor takes a start-fn, stop-fn and the function The start and stop funcions take this as their only argument (records are open,both settings and dependencies will be folded in) Implement IFn with the function, state and arguments

seancorfield19:07:43

Re: multiple Components in parallel -- we have some tests that rely very specifically on being able to spin up a "cluster" of systems in a single JVM to test certain behaviors. We have quite a few tests that use mock Components of some description as well. So that's an important ability for us.

πŸ™‚ 3
dominicm19:07:50

I've worked on a lot of projects, and as a consultant I jumped around projects, and unpicking people's clever tricks for assembling systems was no fun. I was explaining clip to someone yesterday, and there's a whole iceberg beneath clip of what I'm trying to achieve that's a little deeper than what's on the surface. Problems caused by the infectious nature of component, and also problems like request stuffing in ring (which I have blogged about already). I'd like to start marketing clip at some point in the future more. But I want to put AOT in place first. I've got about 5 different implementations for how AOT could work, but it's a little complex due to future extensions I'd like to support.

seancorfield19:07:10

Re: metadata extension -- next.jdbc leverages that for its built-in Component support (without needing Component as a dependency, BTW): https://github.com/seancorfield/next-jdbc/blob/develop/src/next/jdbc/connection.clj#L318-L326

πŸ‘ 3
borkdude19:07:44

oh I have used this outside of component, just not with it

borkdude19:07:02

oh like that, that's nice!

seancorfield19:07:03

@U09LZR36F Since you're here: it looks like Clip "evals" expressions, based on just the README. I assume it doesn't actually use eval, right?

dominicm19:07:03

^ this is a place I need to figure out with clip. Clip doesn't need any support from libraries. But that means libraries don't market clip for you πŸ˜›

dominicm19:07:29

@U04V70XH6 It doesn't use eval no πŸ™‚. It's basically just apply calls under the surface.

seancorfield19:07:16

I'll put Clip on my (long) list of projects to take a deeper look at. I just get the impression from some of the Juxt projects that you almost have to buy into "more Juxt" to get the best out of them since several of them seem to recommend each other for various things... entirely possible that's just my (mis-)reading of the docs though...

dominicm19:07:03

@U04V70XH6 https://github.com/juxt/clip/blob/3375cb1564cc1d0d887bf6ebfb530b656c0dc422/src/juxt/clip/impl/core.cljc#L149-L188 here's the implementation if you're curious. It's very similar to SICP's metacircular evaluator. My v1 of clip was literally just apply, but I found a handful of counter-examples where that was insufficient and went down a rabbit hole of implementing those. In practice, the implementation is simple enough.

dominicm19:07:31

@U04V70XH6 yada definitely implies bidi. bidi doesn't imply yada though. Crux is also standalone. clip was designed to play nice with edn, which aero happens to provide quite nicely. However, I've also used it with cognitect/fern successfully, and also in pure code for Cljs purposes. If you do give clip a spin, let me know. I'd be very open to your thoughts on it. Another benefit of not pushing it much yet is that I'm happy to break the API still.

lilactown20:07:27

I'm curious to get y'alls initial thoughts on an approach like this to constructing a system and managing start/stop of resources by encapsulating them in what I'm calling "modules" https://gist.github.com/lilactown/0d6c842e8a36c989c1f52ad06f1c9f97

hiredman20:07:35

Requiring things to be def'ed is bad, ideally your core functionality should be usable without introducing any global bindings. And then layer defs on top of that to improve the UX. Introducing a new def form is something of a red(or at least yellow) flag in my mind

hiredman20:07:37

This is one of the reasons the metadata protocol extension is such a great addition to component, gets you out of reliance on defrecord

βž• 6
dominicm20:07:31

@U4YGF4NGM https://gist.github.com/lilactown/0d6c842e8a36c989c1f52ad06f1c9f97#file-system-clj-L34-L35 this line looks dangerous to me. if (kafka) fails, then you've got a lingering (database) hanging out. That can lead to connection exhaustion in databases. I think the postgres limit is ~40 by default or something. So if your connection pool opens 8 of them, that's 5 resets not working before everything goes πŸ’₯

dominicm20:07:51

I experimented with the idea of a reversible let binding as a design for clip, something like:

(cliplet [db (database foo) (stop-db db)
          kafka (kafka db opts) (stop-kafka kafka)]
  ??? continue with program, run a test, whatever
)
If it hit a failure anywhere, it would just reverse-order stop everything up to that point. I still think it's a worthwhile design to look into, but there're questions around duplication, prod vs dev vs test systems, etc.

seancorfield20:07:17

@U09LZR36F In practice tho', if your components fail to start, your process is probably just going to exit and that will clean up pretty much everything.

dominicm20:07:23

@U04V70XH6 In production, yep! But not during development if you're using a reloaded workflow (which I find optimal for some kinds of changes)

seancorfield20:07:05

Do you run in component start failures enough in dev for that to be a problem? It's not a problem I've run into...

seancorfield20:07:46

(but I am not constantly restarting components -- I tend to start my system up in my REPL and leave it running for a long time while developing against it)

dominicm21:07:44

That's a tricky question to untangle. It might not be something you hit weekly. But on the day you do hit it, I expect it will take several iterations before you get something new right.

dominicm21:07:49

If you were willing to kill the REPL in the case your system failed to start, you could probably get away with:

(ns foo)

(def db ...)
(def kafka ...)

(def http-server (make-handler db kafka))
(still no vars referenced except in gluing your system together, so only internally)

lilactown21:07:17

@U09LZR36F the idea have is that it would close the db conn, but the fix for that is hidden behind the api; the idea is that the (database) call behind the scenes adds itself to a buffer of resources that were started in the current load! of a module and if any of them fails it catches the error at the module boundary and stops any that are in the buffer

lilactown21:07:49

the idea is basically "what if react but for services"

dominicm21:07:39

πŸ˜„ You're up to magic.

alpox21:07:41

I was working with some services where the whole system should still be able to function at least partially if one component was/is not able to get going

alpox21:07:14

Shutting down the whole system if one service cannot start is not always an option

dominicm21:07:36

Yep! Clip currently gives you a partially started system you can choose to stop.

alpox21:07:15

Sadly clip doesn't seem to be much of an option for me to my current needs - but i'll keep it in mind for later! I have a case of distributed configurations that should be pulled in whenever necessary and can come from theoretically anywhere - and those configurations may have to start registered services or old ones have to be shut down. Currently the design of integrant fits my needs the most and I can get it going without much (just a little) fiddling. I may roll-my-own sometime to get rid of the fiddling.

dominicm21:07:17

@U6JS7B99S over integrant, clip is a small improvement. If integrant works for you, definitely stick with it. Clip has most features Integrant has. Is clip missing something you'd need to pull that off?

alpox21:07:15

@U09LZR36F from what I see i'd need explicit :start and :stop annotations as well as direct service calls within the config which I really don't want to see in my configurations. The configurations can also be created in yaml or json. Configurations should best not have any "system" or "environment" knowledge except for their dependencies and what they represent. -> I'm a "mapping" component and this is my configuration. Thats it.

alpox21:07:12

I may be missing something here though and have to take a closer look :thinking_face:

alpox21:07:06

All that to say: What I'm missing with clip is the disentanglement of configuration and lifecycle

dominicm21:07:36

@U6JS7B99S you could totally write a json->clip transform though, no problem. Same way that you would for json->integrant really. But you're right, clip puts the function you need to run to start a component at the forefront of the api rather than behind the indirection of a multi-method or record.

alpox21:07:59

@U09LZR36F Atm I still fail to see how to represent a clojure functioncall for start/stop into json apart from a clojure-containing string? As reference: My ideal model for a configuration would look about like:

{:foo/bar {:type :foo, .... <configuration keys>}
 :foo/baz {:type :bar, :some-key #ref :foo/bar}}
Where the :type defines the kind of component - or if missing it represents a simple configuration map (No component)

dominicm21:07:59

(map-vals #(merge % (case (:type %) :foo {:start `(foo)} :bar {:start `(bar)})))
Would transform that format into something clip can understand. Similarly to how you need to transform it for other things.

dominicm21:07:21

Or you could look it up somewhere. There's no magic to going from :foo to (foo). You need some lookup system. In Integrant, it's multi-methods that run code. In clip it's a symbol that references a function.

alpox21:07:06

Hmm true, but it would need a bit more than that as i'd need start/stop, runtime replacement and :foo/:bar probably not matching a function. I may give it a whirl to see if I can get it πŸƒ with clip

alpox21:07:39

I can see that I could probably transform it somehow in the right format

dominicm21:07:04

If you're being super dynamic, it's probably much of a muchness anyway πŸ™‚.

alpox21:07:06

With integrant it was actually only a very few lines (the only thing necessary was actually setting up derivations from keys to component types)

alpox21:07:23

But its not really something I like to do. But for setting up composite keys I'd have to postwalk all configurations for fixing references so that was also something I wanted to avoid as with many configurations that is not optimal

alpox21:07:05

@U09LZR36F So then thanks for the insights! I now have the idea of how to use clip as basis, that might actually work out quite well if I manage to make component replacements on running systems work (a'la integrant/resume). I believe it should be possible to build components through multimethods/records on top of clip.

dominicm21:07:20

heh, I'm not sure if it's worthwhile, but you could definitely do something like:

{:something {:start (integrant.core/start-key :something {})}}
Just for funsies.

πŸ˜… 3
Ben Sless13:07:18

Add it to component, we shall build the ultimate library, InterClipCom

Ben Sless13:07:46

Powered by the -><-->>?as-|> macro

πŸ˜„ 3
p-himik14:07:33

> Sorry,Β http://interclip.comΒ is taken. :(

πŸ™ƒ 3
😭 3
kristof18:07:12

has the bay area meetup group stopped doing monthly ones?

seancorfield20:07:50

That's a bit of a random (and local) Q for #clojure but, yeah, I think everything stopped with the pandemic and never really got going online... #clojure-sanfrancisco is where announcements tend to get made.

seancorfield20:07:40

Looking at the archive of events on Meetup, it looks like I signed up to attend in April and May but I don't remember how those went...

seancorfield20:07:45

I doubt I would attend in-person -- it's too much of a hike into SF from the East Bay at the end of the work day for me but I've been attending virtual meetups in Seattle, Los Angeles, and for a while Provo (but that's stopped meeting since one of the organizers moved away I think) @UJHMA11DJ

πŸ‘ 3