Fork me on GitHub
#clojure
<
2019-05-10
>
markx04:05:18

I'm trying to write a small wrapper of jline, but git lost in deciding the pattern. Do I wrap the constructor and provide functions that takes an object a parameter, or do I instantiate and hide the jline class in the wrapper namespace in a var or atom, and provide functions that implicitly use the hidden object?

noisesmith17:05:13

forcing a global singleton on an end user is pretty much never a good idea

markx04:05:04

Could someone point me to some tutorial or examples?

lilactown04:05:32

I think that depends on the answer of: would you ever need two instances of the jline class?

cjohansen04:05:18

At some point, the answer to that is going to be yes. I would not hide the object. If you do, then provide multi-arity functions: one without the object that uses your ns def and one that takes the object explicitly

markx04:05:14

If I don’t hide that object, the code to use it would be something like:

(def line-reader (mywrapper/line-reader))
(mywrapper/readline line-reader "this is a prompt")

markx04:05:05

Does this look good?

lilactown04:05:32

yep, that seems good 🙂

lilactown04:05:13

maybe you could provide a 1-arity version that automatically constructs the reader

lilactown04:05:37

I’m not familiar with line readers or jline specifically, so it’s hard for me to comment

markx04:05:42

I somehow feel it’s smelly, but don’t know where and how to fix it. Probably because it’s also side-effect code.

noisesmith17:05:55

pretending something that is intrinsically about side effects is somehow functional is a bad idea without a global system for managing state. We don't and won't have a global state management system in clojure (jline is there for its side effects, right?)

lilactown04:05:15

like take e.g. a database library. you’ll almost always create a db connection object, and pass that to all of your other functions (query, transact, etc.)

lilactown04:05:41

I guess another question could be: what would happen if you created an object every time you did something with the jline class?

markx04:05:36

I don’t know. That might work, but then I will need to do the same for all the util classes, like completer, highlighter and maybe history handler.

markx04:05:51

then I’ll have to save the states, for example completion candidates, outside the object, like in an atom or something.

markx04:05:51

Hmm I just feel java and clojure don’t work alone well.

lilactown04:05:05

sounds like you have some mutable resource you’re managing, which you should provide as an object you can pass to the various functions

seancorfield05:05:55

Wrapping an OOP library with a function layer that creates and passes the objects is really no improvement.

seancorfield05:05:18

If you can figure out how to wrap the library with data and a simple function API, you have a win.

seancorfield05:05:38

Otherwise you might as well just use the underlying Java OOP library with interop.

markx06:05:00

Yeah that’s how I feel, not really an improvement. It’s just (.readline obj) vs (readline obj)

markx06:05:44

@seancorfield But what do you mean by wrap with data and simple function API? Could you give me an example?

devn08:05:02

I think a more accessible version of what’s being described is something like hiccup or ring.

Wes Hall09:05:40

@markx you may find ‘memfn’ from the core library a useful too when creating these thin idiomatic wrappers around Java libraries. I agree with @seancorfield, re: not getting too deep into the rabbit hole. It’s entirely possible when doing something like this, to write a lot of code that really doesn’t end up improving the situation, but ‘memfn’ can often be used to create a very thin layer that allows the calling code to appear slightly more idiomatic.

wei09:05:48

someone mentioned to me it's possible to add a dependency to deps.edn and not have to restart the java process. is that true, and how would I set up my app to do that?

slipset09:05:40

It is true

borkdude09:05:55

@wei it’s called add-lib, it’s a alpha-omega feature

Wes Hall09:05:06

What's "alpha-omega"?

borkdude09:05:43

@wesley.hall it’s more alpha than alpha

Wes Hall09:05:48

Haha! Yeah, honestly the thought had crossed my mind before that by calling all these things "alpha", there was going to be no names left for stuff that has yet to make it into the alpha. Cache invalidation and naming things 😛

slipset09:05:11

And off-by-one errors

borkdude09:05:26

OutofNamesException

slipset09:05:05

On that note, my son told me (when I praised him): If everything I do is great, everything I do is average.

borkdude09:05:10

can’t argue with your son

roklenarcic11:05:09

When using go-loop in core.async... can the logic in the loop occur concurrently... by that I mean if I have input channel with 10 items, and I poll the channel in go-loop and process an item, will the items get processed concurrently?

tianshu12:05:18

Why when-let only support one pair binding?

tianshu12:05:39

@roklenarcic nope, If you have only one go-loop, it will process item one by one. If you want parallel processing, you can use pipeline or pipeline-blocking.

roklenarcic12:05:05

So basically that is about the same concurrency as if using a single agent

jaihindhreddy12:05:15

Yeah. An agent will have an actual thread though so there's no overhead of IOC threads.

roklenarcic12:05:32

So the benefit of core.async is only there if I spawn a lot of go-blocks, because they can share a small number of threads

roklenarcic13:05:55

So if I have like 1 continuous process per user and I have 1000 users, then I should make a go-loop for each person and it will work with limited number of threads?

jaihindhreddy13:05:30

But if you have a bunch of independent things to do. This might not be the best approach. pmap and reducers might be a better way to go about it.

jaihindhreddy13:05:03

core.async is more appropriate when the processes have to communicate with each other (using channels)

roklenarcic13:05:08

The biggest obstacle to naive implementation for me is that some processes have to be throttled in my case, and if I use naive sleeping in the process, then it takes up a thread.

john14:05:55

Yeah, core.async is good for that too

tianshu14:05:22

@roklenarcic you should not sleep in go block.

roklenarcic14:05:12

I know, but I think I've found a solution

john14:05:25

You usually don't have to worry about sleeping in go blocks. When waiting on channels they sleep automatically.

Noah Bogart15:05:34

I have a quick question about a micro-benchmark i'm running

Noah Bogart15:05:26

using https://github.com/hugoduncan/criterium, i'm checking two different versions of the same code, and i'm confused why the single-pass version that uses reduce is slower than the version that does multiple passes with filter and map

Noah Bogart15:05:22

reduce version:

(->> (server-cards)
     (reduce (fn [acc card]
               (if (ice? card)
                 (concat acc (split (:subtype card) #" - "))
                 acc))
             [])
     distinct
     sort)

Noah Bogart15:05:23

(server-cards) returns a list of maps, ice? checks the :card-type attribute to see if it's an ICE type, :subtype is a string of course

Noah Bogart15:05:56

filter/map version:

(->> (server-cards)
     (filter ice?)
     (map :subtype)
     (mapcat #(split % #" - "))
     distinct
     sort)

Noah Bogart15:05:42

are they doing different things, and i don't realize it?

Noah Bogart15:05:30

(->> (server-cards) (reduce (fn [acc card] (if (ice? card) (conj acc (split (:subtype card) #" - ")) acc)) #{}) sort) (conj and #{}) seems to be the fastest, at roughly half the time of the filter/map version

Noah Bogart15:05:27

Ah, it's the repeated calls to concat in the reduce version. using conj with a '() makes it faster than the filter/map version, from a mean of 1 ms to about 770 us, and using a set instead of calling distinct at the end reduces it further to 600 us

Noah Bogart15:05:36

well, thanks for listening, y'all! lol

vlaaad15:05:08

Side note: I really like combining criterium benchmarks with clj-async-profiler, so I can see both time spent and where it was spent

CyberSapiens9716:05:50

Does anyone know a good way to auto generate API requests/responses tests by making use of an OpenAPI 3.0 specs?

CyberSapiens9716:05:57

I've been searching for some kind of solution based on this, and i've found in other languages, but nothing using Clojure, i thought it would be nice for me to try to create a lib/lein plugin that could do this

CyberSapiens9716:05:40

I REFUSE manually writing tests for my API Endpoints since i have it well documented using OpenAPI 3.0

CyberSapiens9716:05:36

and certainly i could generate automated tests for it because the specs from OpenAPI have all the necessary info to do so

CyberSapiens9716:05:32

i just want to make sure that if i could make something like this in clojure, this would be appreciated or used by other Dev's

lilactown17:05:51

I'm not sure you need this to be in Clojure? It sounds like you want to send requests based on a Swagger spec, right?

Nick Stares17:05:46

I had a interesting conversation recently with two people who work in IT/networking/cybersecurity who said they had mostly given up on programming as a frustrating and tedious task (having programmed only in Java and Python). I tried to sell them on Clojure and the part that interested them the most was the idea that programming could be interactive via the REPL. I told them about how you can build programs while evaluating each form inline and debug by revisiting your small pure functions instead of stepping through with breakpoints or printlns. Good reminder to focus on emphasizing interactivity when evangelizing as it truly is the most intuitive way to write programs!

4
mloughlin17:05:26

I don't know anything about Java HTTP servers, how do I pick between the available Clojure ones? 🙂

lilactown17:05:58

go with Jetty if you're just starting out. it's fairly easy to get started and well supported

lilactown17:05:04

lots of guides + examples using Ring and Jetty

CyberSapiens9717:05:25

i started with Ring also

CyberSapiens9717:05:50

in fact i didn't knew anything about backend development when i started with clojure. i learned by looking to real world examples

seancorfield17:05:21

We use the embedded Jetty server (with Ring, Compojure, Component, and Selmer -- for HTML templates) at work -- heavily in production.

CyberSapiens9717:05:54

@seancorfield when building API's, do you ever make use of Selmer/HTML templates?? i don't see how this would be possible/useful when building REST apis, just asking to know if i'm missing something

seancorfield17:05:43

@cybersapiens97 No, if we're not producing HTML pages from a web app, we don't use Selmer.

seancorfield17:05:06

Well, we also use Selmer to render HTML emails that we send to our members -- so we use Selmer inside REST APIs for that.

CyberSapiens9717:05:16

oh nice to know

CyberSapiens9717:05:55

do you ever built a medium/large product directly rendering HTML in server?

CyberSapiens9717:05:12

i mean, entirely

CyberSapiens9717:05:39

i imagine if this is easier to maintain than a REST API

mloughlin17:05:00

I think it's easier for a small/1 person team because you don't need to maintain a separate front-end build process

mloughlin17:05:27

and you get SEO and page load speed benefits for free

seancorfield18:05:59

@cybersapiens97 Prior to Clojure, our main member-facing app was server-side-rendered HTML. We have a few smaller apps now that do that (in Clojure, with Selmer), but we've mostly replaced the SSR HTML with a React.js-based SPA backed by a REST API.

hlship22:05:59

I had an idea about extending one protocol onto another protocol; doesn't seem like that's doable. I know it opens up a rats nest of inheritance issues, but still feels like something useful. My scenario was to define a protocol InterceptorComponent that defines some callbacks, then extend io.pedestal.interceptor.IntoComponent onto it. My imagined behavior is that this would be effectively equivalent to extending IntoComponent onto any record that implements the InterceptorComponent protocol.

lilactown23:05:49

so basically, IntoComponent uses InterceptorComponent’s methods in some way and as long as a type implements InterceptorComponent, you can also use IntoComponent methods?