Fork me on GitHub
#clojure
<
2019-11-28
>
didibus01:11:30

Question, how do you have cond-> fail if none of the conditions matched ?

seancorfield01:11:11

@didibus I'm not sure I understand your question... (cond-> foo false (explode)) is just going to produce foo -- it'll never call (explode foo)

seancorfield01:11:54

The set of conditions in cond-> isn't an exhaustive list... there's no "else" equivalent (other than the original expression being returned untouched).

didibus01:11:52

Ya, I was kind of hoping there was an :else of some sort to it

didibus01:11:02

So I could do: cond-> a? a b? b c? c (not (or a? b? c?)) x but without having to write that last one myself

dpsutton02:11:19

if you thread something through that you can check against on the outside you could error if it was unchanged

didibus02:11:04

Ya, except if I'm threading a crappy java object which is super annoying to test for if it has changed or not šŸ˜›

dpsutton02:11:34

You could use the cond-> to build up data about what to do to the object rather than doing it to the object. Then the lack of actions would be the error rather than the lack of mutation

dpsutton02:11:50

Also way more testable that way

vemv03:11:24

You could create a custom cond-> macro that asserts that at least one condition matched. FWIW I have sth similar: a cond replacement that asserts that exactly one (not zero, not N) condition matches

didibus04:11:02

Thanks all. I just lived with a bit more repetiton

kaosko06:11:54

I'm trying to manage latency over a Sente (buffered websocket) channel. I already have priority channels before it, i.e. sending control/priority messages are always preferred over the bulk of the traffic send over the "data hose" channel. the data should be low latency, I don't care if too old data gets lost (sliding buffer). As is, I don't have mechanism to prevent latency going up as the buffer fills up. How do I best monitor the buffer/build something in between my two channels and the actual send buffer? Lots of ways to skin this cat, figured I best ask for opinions before rolling up my sleeves

Tom Fenton10:11:00

Morning! Has anyone else had success making Clojure applications run as Apache Daemons via prunsrv? I've been going around in circles for about four hours and I'm fairly close to the throwing-things-at-the-wall stage...

Tom Fenton13:11:42

For anyone else unfortunate enough to try using Commons Daemon - looks like prunsrv only recognises static methods, even though the Daemon interface methods aren't static. Ended up defining the interface myself with (:gen-class (:methods ...)); the documentation also gives the wrong arities for all functions, so this has just been a whole mess of trial and error.

ido13:11:52

hey!, does anyone has an idea if https://github.com/clojure-goes-fast/clj-async-profiler can be used as a command-line process to profile an external process? if so, how?

mping13:11:14

the profiler operates on java code with a jvm agent

mping13:11:23

are you asking for profiling clojure apps or any kind of app?

ido07:11:31

specifically clojure apps. I am trying to figure out what would be the most convenient way to use it as profiling instrumentation tool in production. I guess that the intent was to sue it as a project dependency so it can run a small http server interface that can start/stop profiling and later serve the svg files for the flame graph. what do you think?

cjohansen16:11:27

Does anyone have experience consuming a SSE event stream in Clojure?

cjohansen16:11:44

Client in Clojure, not server

littleli17:11:50

You mean 'server sent events'?

cjohansen19:11:40

Yep. Basically it's just a long-lived HTTP request where you consume the body line for line

ec19:11:57

hey, lets say I am building an app which has text editor (not that important) since there is many text editor providers I'm not so sure about my current decision. To store the content I need a DB, again so many options. But I know what Im expecting from a DB and a editor so my first instinct is using defprotocol defining required funcs, to be able to change and try other providers easily without tinkering too much with the code. Anyway what I'm not sure is this, am I still forcing myself thinking in ~OOP in times like these? What would you do ? It just seems intuitive. Is there any posts/sources that talks about design parts of things espically with comparasions? I'm aware of http://mishadoff.com/blog/clojure-design-patterns/ , but something more focused would be nice.

weavejester19:11:32

I donā€™t think this is you thinking in OOP. When I interact with external services, I often put them behind a protocol, both to isolate the calls, and to allow me to stub or mock them out during testing.

weavejester19:11:47

However, be careful about overengineering. One of the benefits of protocols is that you can replace functions with protocol methods without changing any of the calling code. So you can always replace a set of functions with a protocol later.

šŸ‘ 4
didibus20:11:46

Clojure embraces abstractions and polymorphism. OOP has it's own mechanisms to provide those, and that's these concrete mechanism that are often criticized, and not the concepts

didibus20:11:32

If you are in a scenario where you have two+ of something that can be abstracted cleanly behind a Protocol, go for it.

didibus20:11:59

That said, do you expect that you will actually actively use two+ of those things in practice?

didibus20:11:48

Cause if not, just write your functions and keep refactoring them as you try out new DBs until you find the one you like.

didibus20:11:05

The danger of an abstraction is that it comes with a higher burden. You need to find an abstraction that fits all DBs equally well. And that can be hard. For one DB, you might realize that they just don't fit the abstraction as well as you thought, or they support things that would require a different interface and means of interaction, which your Protocol might not be able to mold itself too.

ec20:11:19

Thx for tips and comments. What about state? Shove it inside a namespace atom then write public functions that derefs it is the general idea? Or is there any other recommendations for encapsulation?

didibus21:11:56

There's many different strategies for handling state. But generally encapsulation of data isn't encouraged.

didibus21:11:34

In general you want state to be immutable. So don't use an atom. Your namespace just shouldn't hold any state at all

didibus21:11:04

Every single function simply takes as input whatever it needs, and returns as output the result

weavejester21:11:12

Yep, Iā€™m pretty sure Clojure explicitly rejects encapsulation in the official rationale.

didibus21:11:50

Let the user of your namespaces choose how to manage state if there are any

weavejester21:11:08

What sort of application are you making, @UFHB0T69M? If you have a database and a text editor, it sounds like it might be a webapp?

didibus21:11:41

An example would be. If you would have had a class with fields x, y, z. And your methods would use those fields. Well now just add to each method an argument for x, y and z and the user of the class should simply provide these and make sure to carry them along from one call to the next. In this case the class being a namespace, but devoid of state.

didibus21:11:12

In doing so, state will be pushed to the top layer. And the top layer can choose how to manage it. That could be done by putting it all in a map inside an atom. Or it could be done by capturing it inside closures. Or by carrying it through passes of a recursion. Or putting it inside agents, or refs, etc.

didibus21:11:15

If you have things that are more like entities. Those can be modeled as a map or record. So your functions instead of taking an x, y, z, could take a map of x, y, z, and return that.

ec21:11:08

@U0BKWMG5B yeah its actully a web-extension note taking app. Requires interop with npm pkgs etc. But I dont want to be specific, used just for example. @didibus thx for the explanation, so if you were to write some funcs that, say requires a connection or any stateful thing, your all func would take that as param thus state management pushed to upper level?

šŸ‘ 4
didibus21:11:43

The exception to this rule are constants. Those can be in your namespace if you want as top level defs, as long as they are read only. Though sometimes it's nice to take those in as well, so in tests and all you can override them easily.

didibus21:11:25

Ya, pretty much.

weavejester21:11:41

Thereā€™s a common pattern where the applicationā€™s state is stored in a single atom, and functions are written that take in a state and an event, and return a new state.

weavejester21:11:01

(f state event) -> state'

weavejester21:11:28

This can then be applied to an atom with (swap! state-atom f event)

ec21:11:34

Yep I am familiar with that kind of architecture by elm reframe redux etc..

ec21:11:55

Just didnt see much that kind of things on the "backend"

didibus21:11:15

Ya, that's one good and proven pattern. Just be sure when people say you pass state, they mean the immutable structure inside the atom, and not the atom itself

didibus21:11:42

On the backend, ask yourself if you can't just be stateless instead

didibus21:11:54

Your state being managed by your DB

weavejester21:11:19

Just what I was about to say šŸ™‚. The only state I ever tend to keep are caches, and even then thatā€™s rare.

4
didibus21:11:24

You can even start by just creating a new connection to the DB on every request.

didibus21:11:45

And deal with improving the performance of that once you get to that being an issue

ec21:11:28

Thats the one thing that concerns me about all this, yeah early optimization is blabla right but making it second class like this, might isnt it hard to untangle when it accumalates. Idk if all these passing, recreating(new structs share common parts tho) can be optmized away via JIT

didibus21:11:49

Which like @U0BKWMG5B said, it just really all amounts to caching.

didibus21:11:27

Well, if you did it right, everything that would need to be cached will live at the top most layer.

didibus21:11:09

So its trivial to refactor that part to have some caching in place.

weavejester21:11:10

Performance in FP is often a caching issue. Imperative languages tend to complect this - you modify in place, or you hang onto data instead of recalculating it. But essentially theyā€™re all different mechanisms for caching results, just somewhat obscured.

didibus21:11:52

I wouldn't say to start without caching anything at all from the start if you weren't learning Clojure. Eventually, you discover your preferred mean for organizing components at the top layer. Some people use a component framework like Component, integrant, mount, etc. Some people just use defs. Others might use delays and atoms. Some might use closures, higher-order-functions, memoization, etc.

didibus21:11:34

But without knowing these, you'll be confused and distracted. I mean, my opinion off course.

didibus21:11:08

You might find caching your connections for example is an okay thing and gives you a bit more performance. But maybe putting a shared proxy cache instead is even better for performance!

weavejester21:11:20

I think Iā€™d go straight for a connection pool like HikariCP rather than trying to cache connections bespoke. Just to nitpick šŸ™‚

ec21:11:33

I'd love to read you guys articles about design comparasion of a generic programmer and a clojure programmer (said clj since not all func. langs have the same tools) on an real_world_tm app parts.

didibus21:11:44

It depends. If everytime your app does a read or write it is a whole new query. Then a connection cache aka a pool might be better. But if your app is read heavy and most reads tend to be duplicate requests, then a request cache could be better. Etc.

ec21:11:17

thx again

šŸ‘ 4
didibus21:11:39

Caches are hard though. That's why I recommend not starting with them. Get a grasp on everything else first. What's the famous quote? The hardest thing in computer science are caches and names?

didibus21:11:32

In OOP, every object is a cache šŸ˜ no wonder it's so darn hard

weavejester21:11:21

Thatā€™s an interesting way of thinking about it. Iā€™ll have to remember that one.

weavejester21:11:41

The ā€œevery object is a cacheā€ I mean.

didibus21:11:30

šŸ˜thx

didibus21:11:44

And it gets even more complicated fast. People start having object caches as well. And suddenly many things are sharing mutable objects with one another believing that they have a new fresh copy, when they don't. Those bugs have always been some of the hardest to debug

ec22:11:48

> What's the famous quote? The hardest thing in computer science are caches and names? n OOP, every object is a cache šŸ˜ no wonder it's so darn hard Smooth.