Fork me on GitHub
#beginners
<
2017-04-14
>
mbcev00:04:30

@noisesmith , I thought of that too and figured it would be the best way. I've begun building my functions out that way but I guess my concern is, should I use a future for a single thread of my app? Basically what I have is I get a connection from a client, and then I want to just continue processing data coming in and going out of that stream indefinitely until otherwise noted.

mbcev00:04:57

But somehow I guess it just mentally feels odd that that's how I would go about handling the process? Maybe that's just in my head though.

noisesmith00:04:10

that seems reasonable except thread-per-client is fragile (unless you strictly limit client count)

mbcev00:04:44

Yeah this isn't a billion client application. Just something really simple with 5 clients connecting into my server app sending data back and forth.

noisesmith00:04:05

you can use stuartsierra’s excellent component library to manage data and resources and state that need to be shared between code in different namespaces without needing to create global singleton data

noisesmith00:04:31

there are other alternatives people have made as component replacements too, but component is the one I have used and it works well

mbcev00:04:06

Awesome I'll take a look at that as a jumping off point; thank you.

john00:04:13

So I just ended up calling the main source directly, instead of the stub file. Seems to have worked.

didibus01:04:08

@mbcev I feel like you've got the perfect use case for Dynamic Vars

didibus01:04:48

@mbcev You need thread global variables correct? If so, that's what those are for exactly

mbcev01:04:44

@didibus actually what I was aiming for was the exact opposite. I wanted to make sure my data was isolated. What I had been doing was clumping all my "processing" code in one namespace for organizational purposes and then while repl dev-ing I created an atom that I could work on as I added/tested functions. But I don't need the atom anymore now that everything works as intended. I just pass the vector of maps in as a functional argument and my functions pass that around and work with it as necessary.

mbcev01:04:10

But when I was looking at stuarsierra/compontents and doing some more research I actually stumbled on Dynamic Vars.

mbcev01:04:24

So I appreciate the input also! They're not what I need for this project, but I've added them to my toolbelt!

didibus01:04:39

@mbcev Well, you want it isolated at what level? At the thread level, or more granular?

didibus01:04:58

They'll give you thread isolation mutable globals

didibus01:04:05

thread isolated*

mbcev01:04:35

@didibus well I don't know what is idiomatic I guess, but the way I'm "used to" thinking about things is the "everything is an object" world of Java. So when it comes to organizing my code now I have to consciously make an effort to remember that a namespace isn't equivalent to a java class/object.

mbcev01:04:54

So what I wanted was to put all my "processing" code in one namespace and all my networking code in another namespace.

mbcev01:04:10

My networking code gets clients and then spins up a future that starts off the processing code

didibus01:04:49

It sounds like you probably didn't need a global, and that's probably for the best. They'd be useful if you needed multiple functions to share the value, but you did not need to share it between threads.

mbcev01:04:02

Yeah, I don't need any sharing between the threads in this case.

didibus01:04:15

What about between the functions?

didibus01:04:37

So, dynamic vars do not share across Threads. They're a bit like a ThreaLocal

mbcev01:04:21

In a Java Class/Object what I'd do is have my private ArrayList MyObjName let's say and then my functions could be written to freely call MyObjName without me needing to worry about Client 1 messing with Client 2 because all the code and data is encapsulated

mbcev01:04:18

So I just wanted to figure out what the "right" way of doing that would be with Clojure, what I have ended up doing is having a (start-process [structure, clientID]) function in which I pass my data structure and which client stream and then let the functions do their thing

mbcev01:04:47

so I took out the (def data atom []) that I was using before

didibus01:04:45

Could Client 1 and Client 2 share a thread? If not, you can do the same thing you'd do in Java with dynamic vars. Though it would probably be more idiomatic if you didn't use global variables and had every function take all input as argument.

didibus01:04:18

I feel you ended up with the most idiomatic solution.

didibus01:04:43

Thou just an FYI, dynamic Vars give you thread isolation. They're thread safe basically

mbcev01:04:46

Uhh, I'm sure they could share the same thread. I'm just not good at Clojure yet haha.

mbcev01:04:03

So it just kind of made more sense to me to let them do their own thing.

didibus01:04:03

makes sense

mbcev01:04:34

I'm using ztellman/manifold for the stream abstractions and pooled queues. So honestly I probably didn't even need to use futures the way I did.

mbcev01:04:47

I'm just not entirely familiar with how all the individual components of what I'm doing work so I'm sure a more experienced dev could clean up or optimize what I'm doing.

didibus01:04:13

Uh, didn't knew about manifold, looks pretty cool

didibus01:04:53

Ya, it sounds like you managed to figure out something pretty good on your own

mbcev01:04:47

I know just enough Clojure to be dangerous haha.

didibus01:04:37

You can definitely shoot yourself in the foot. But I think Clojure is pretty good at not letting you do that.

didibus01:04:59

Unless you start writing a bunch of macros

didibus01:04:05

don't do that

mbcev01:04:17

I own a few of the great books out there and have read each of them at least twice, have been following the community for a few years, and have watched plenty of the conferences on youtube so I have developed some sense of "idiomatic clojure", but I don't have tons and tons of actual use-case experience.

didibus01:04:49

Ya, I've done some work projects in Clojure, but even I feel I only know the surface. If its a use case that's different from what I did, I'm lost again. Like never had to use core.async, or do any kind of web work, so these parts are obscure to me.

didibus01:04:12

Barely touched core.logic

didibus01:04:38

Darn the alarm, I can never demo things 😞

mbcev01:04:29

@didibus I'm playing with it now yeah. Awesome! Thanks! Learned something new.

didibus01:04:03

But ya, generally, would be better to just have increment-to-10 take a counter, instead of modifying a global, even though that global is thread safe.

mbcev01:04:08

Yeah 99% of what I'm doing can be done immutably so I don't need a mutable global or anything like that. The only reason I did need to keep track of state basically was to keep track of how much data my client had sent vs how much the server thread had received so that if necessary, the client could go back and resend missing data.

didibus02:04:25

Ya, that's an interesting problem. Like you need a place to track progress, the progress could be updated by multiple threads, and the number of clients is dynamic.

Drew Verlee19:04:36

If i wanted to use the max function to find the max of something that wasn’t already defined, how would i go about doing that? extending the max function?

Drew Verlee19:04:30

i know it would be easy to sort then take the first.

Drew Verlee19:04:20

hmm i guess max is just intended for numbers.

Alex Miller (Clojure team)20:04:15

you might look at max-key