Fork me on GitHub
#beginners
<
2019-06-05
>
skykanin12:06:05

I have a concurrency question. I've built this "n-body" simulator which currently does everything in a loop on a single thread and I now want to make it concurrent. All bodies are represented as maps with a mass, x y pos and a velocity vector [vx vy]. Three things need to happen. 1. Calculate forces applied to each body and update their velocity 2. Update the position of all bodies 3. Draw all the bodies What I'm wondering is should I use Refs or Agents for this?

danielneal12:06:44

That sounds like a hard problem to multithread. Where do you draw the boundary between what things can be calculated on separate threads.

mfikes12:06:04

@nicholas.jaunsen It might be worth looking at Rich's old Ants demo for inspiration

mfikes12:06:15

Step 1 seems parallalizable, then join, and step 2 parallelizable

jayeldoubleu13:06:58

I am sure I am doing something dumb here and any help would be appreciated. I am trying to setup a http-kit server with compojure routing. If i curl the routes i get a 404 from http-kit. The relevant code and project.clj is here: https://gist.github.com/jldoubleu/d2f7d54d49875f0682831c792c31f7bf I was trying to follow the example outlined here: https://www.http-kit.org/server.html#routing.

jayeldoubleu14:06:55

solved. was missing an argument from the route definitions.

Mario C.18:06:27

when to use go-block vs thread vs thread call? I know the second two return channels but why use what you pass in to thread and thread call in a go-block?

Mario C.18:06:02

All 3 return channels actually

ghadi19:06:11

@mario.cordova.862 thread vs thread call -- what is thread call?

ghadi19:06:23

core.async/go vs core.async/thread vs. _ ??

Mario C.19:06:54

core.async/thread-call

ghadi19:06:30

so the 2 thread things are basically the same, one is a macro, one is a function

ghadi19:06:07

(thread (do-some-stuff) (do-more-stuff)) => (thread-call (fn [] (do-some-stuff) (do-more-stuff)))

ghadi19:06:13

same semantics

ghadi19:06:19

a real java.lang.Thread

ghadi19:06:20

use that if you are doing blocking calls or any direct I/O -- use go if you want a 'virtual' / 'green' thread if the only blocking calls you make are interactions with channels through <! >! or alts! alt!

Mario C.19:06:56

If I am making an API call that takes 30 seconds to respond, should I be using the thread?

ghadi19:06:01

you shouldn't be doing any I/O in a go block, not even a print statement

ghadi19:06:15

yes, if you're invoking (http/whatever ....)

ghadi19:06:52

if you want to do that from a go block, have your go block call something that calls thread/thread-call

ghadi19:06:15

(go (<! (something-that-returns-a-channel...)))

ghadi19:06:29

pointless trivial example ^

ghadi19:06:03

the reason is that go blocks are scheduled in a thread pool, and I/O that isn't channels can starve the pool or deadlock

Mario C.19:06:06

Got it... Why are Threads better than Go for http calls?

ghadi19:06:29

it's not about http calls specifically, it's about how go blocks work

ghadi19:06:55

wherever there is a <! >! or alt!, a go block gets internally cut up ✂️ into pieces

ghadi19:06:21

when a go block reaches one of those operations, and it blocks, another go block will resume in its place

ghadi19:06:26

if you do something like a direct 30 second call in a go block, it will prevent other go blocks from running

ghadi19:06:51

the scheduling machinery can only reschedule at <! >! or alt!

Mario C.19:06:54

Interesting, I didn't know... Just knew that it blocks at <!. I gotta dig deeper into this

hendore19:06:15

Is it idiomatic to create namespaces for entities when working with clj/cljs? For example in a simple domain that has a Task entity and a Task List should I have two namespaces like so

hendore19:06:17

;; src/taskmanager/task.cljs
(ns taskmanager.task
  (:require [clojure.string :as str]))

(defn create
  [body]
  {:body (str/trim body)
   :date-created (. js/Date now)
   :date-completed nil})

(defn completed?
  "Has the task been completed?"
  [task]
  (boolean (:date-completed task)))

(defn close
  [task]
  (if-not (completed? task)
    (assoc task :date-completed (. js/Date now))))

;; src/taskmanager/tasklist.cljs
(ns taskmanager.tasklist
  (:require [taskmanager.task :as task]))

(defn create
  "Creates an empty task list"
  [name]
  {:name name :tasks []})

(defn add-task
  "Adds a new task to the given task list"
  [body list]
  (let [t (task/create body)]
    (update-in list [:tasks] conj t)))

(defn count
  "Returns the total number of tasks within the given list"
  [list]
  (count (:tasks list)))

hendore19:06:50

or would having a single tasks namespace with all the functionality for managing task lists and tasks in make more sense?

hendore19:06:56

Really just trying to understand if this is over engineering or actually good practice

lilactown19:06:36

I offend people with my huge namespaces, so don't take my word. but I probably wouldn't use namespaces for this

4
lilactown19:06:44

that being said, if your implementations start to grow (and continue to diverge) then splitting them in namespaces would be nice

hendore20:06:30

Thanks for your input, I think you may have hit the nail on the head with splitting it up as it grows and I have a better idea of what data structures/functions are related and how

noisesmith20:06:51

if what you want is constructors and methods, we have records and protocols for that, I'd be inclined to refocus the code to be about data flowing through rather than entities, or switch over to constructs that are explicitly designed for entity definition

lilactown20:06:40

I do sometimes wish that Clojure had something like first-class namespaces, which would make stuff like modeling entities as namespaces more tenable 😉 but what noisesmith said is 👌

lilactown20:06:04

it's my OCaml / Elixir bleeding over

ghadi20:06:50

specifically the create and add-task stuff

hendore20:06:19

@noisesmith So focus more on the use cases?

noisesmith20:06:02

yes - my first approach would be to define things in terms of the operations on data

hendore20:06:29

create task list, add task, close task, archive list etc…

noisesmith20:06:53

yes, and if an entity model is an improvement, that would use things that are designed for it - records implementing protocols being my first choice there

noisesmith20:06:19

and it would probably just use / organize the functions I defined in the first pass

hendore20:06:44

I see, I haven’t written any protocols yet, not entirely sure when to reach for them, is it usually when I have a function I want to call on different data types? Being able to close a ticket and a task list (for a bad example) I could have a protocol that defined a close function and create two implementations of it for task list and task or would that be a multi method :thinking_face:

hendore20:06:08

That’s probably a terrible example

eval-on-point20:06:12

they are faster than multimethods iirc, so you want to use protocols when possible (e.g. you only need type based dispatch and not dispatch on value)

noisesmith20:06:49

my usual decision is whether there's a group of related operations (protocols allow that) vs. a standalone polymorphic thing (multimethods allow that, with greater flexibility)

noisesmith20:06:03

vs. a data operation that can be defined using clojure's collection ops that are already polymorphic and optimized and debugged (best option, when possible)

hendore21:06:50

This is all great, thank you. I’m sure it will make more sense as time goes by and I gain more experience through trial and error

heath22:06:33

I’m still interested in possibly playing around with Clojure and Corda. I am looking at https://github.com/corda/cordapp-example/blob/release-V3/kotlin-source/src/main/kotlin/com/example/flow/ExampleFlow.kt and wondering: what is the recommended way to build something like the ExampleFlow… proxy. gen-class?

noisesmith22:06:26

I don't know kotlin - is that inheriting any concrete classes or just implementing interfaces?