Fork me on GitHub

Does anyone have any resources on how to create federated or p2p applications?


if i have a nodejs file can i just read it into my repl as code? or does it have to be packaged somehow to do that?


you can require / load it via the mechanisms node offers - node has its own version of require with its own rules about where files need to be


and for a js file you can use that API directly instead of the cljs layer


@mathpunk if you will be running with node as your js platform, this should be easy to translate to cljs


it's not compatible with the browser of course


cooool thank you


[1 2 (range 3 5)] vector resolves as expected to [1 2 (3 4)] '(1 2 (range 3 5)) , i.e. escaped list, resolves to (1 2 (range 3 5)) which is suprising, because range is not eval'ed. I tried to escape it with or @ but it does not make much sense to me, nor does it help either.


Quoting keeps things from being eval'd, including their internal contents


What are you trying to get as an end result?


if you use ` instead of ', you can use escaping


Practially I can use the vector, but I just want to undesrtand if there's a way to eval the (range) in this context


you can also just use list


(list 1 2 (range 3 5)) - the common newcomer mistake is to think ' is a thing for making lists


you can also use eval explicitly, but the real fix is not preventing the initial eval in the first place, and ' is the thing that prevents eval


yes!, the ` quote works as expected : )


i.e. with escaping: (1 2 ~(range 3 5))`

Daniel Stephens18:07:46

I'm having to interop with some java code, just found that HashMap is not considered a map so I need to turn it into one. Seems I can do (into {} (HashMap.)) and that seems to work, but wondering if there is a preferred way (ideally) that would avoid duplicating the whole map?


clojure doesn't copy the HashMap key and vals, it only creates new map-entry objects to put each pair in


there's very little implicit copying in clojure, as idiomatic code assumes that values are immutable, so copying defensively is rarely needed


also, you can use get on a HashMap, it's just things like conj assoc and dissoc that require clojure maps


(ins)user=> (def hm (java.util.HashMap.))
(ins)user=> (.put hm "a" 0)
(ins)user=> hm
{"a" 0}
(ins)user=> (merge {"b" 1} hm)
{"b" 1, "a" 0}
hm is a java HashMap, the final result is a clojure map, no explicit conversion was needed


(merge used get under the hood, that was fine, didn't require any copying, it does create new map-entry when merging, there's no way not to do that, java class or no)

Daniel Stephens19:07:47

Thanks, I wanted to use keywordize-keys but it failed to change anything since it asks if it's a map, though I've just realised I'll have to also recurively into it first to make it work since it appears to be a map of maps


yes, that makes sense - but also, keywords on input data are often a bad idea


use keywords if you are dealing with data literals from code, of course, but in many cases its simpler and less error prone to keep the original data type of the keys


@ULNRSUK8C and to be clear, that into call doesn't copy the map contents, just the skeleton (though in this case you do need to do so recursively)


if you fear a performance bottleneck, you can keywordize within the into itself, instead of walking/reconstructing the key twice

Daniel Stephens19:07:34

it's just unfortunate that it doesn't fit the same pattern as everything else here if I don't keywordize. I'm getting the value from a kubernetes client api and most come back as beans so I have used clojure/ from-java which gets me keywordized maps. In this case it's a custom resource so I get the map of strings to maps/strings

Daniel Stephens19:07:12

To be honest I'm not sure performance is any issue, I was just wondering if there was a more idomatic way of doing it, so I think I'll do as you say with the walk->into


(defn clojurify [m]
  (into {} (map (fn [[k v]]
                  [(if-not (string? k)
                       k (keyword k))
                   (if-not (instance? java.util.Map v)
                      (clojurify v))])
something like this would help avoid the double reconstruction


(first draft, probably has some shallow bugs)

Daniel Stephens19:07:22

thank you, that's very helpful! 🙂

Daniel Stephens19:07:32

spearate question, why do you prefer if-not in the above? Is it just a style choice to have the recursion last or something?


it would probably be better code if you had a seq of predicate/key-transform pairs and a seq of predicate/val-transform pairs, then you have something reusable for foo->clojure conversion


my first preference is for the first branch of a conditional to be the short one


also, to put recursion last, which tends to be a side effect of that


which makes recurring on the wrong branch stand out


or failing to simplify while recurring, also stands out if you follow that rule


especially since we only allow recur on tail calls, it's intuitive that a tail call should go as far down and to the right as possible


it's kind of a lispy rule of thumb thing

Daniel Stephens19:07:49

makes sense, hadn't thought about the branch length, the recursion symmetry is nice


another aspect: humans construct a stack as they read forms


if the short form comes before the long form, you do less work moving up and down the stack :D


(Germans likely have more practice with stack operations and are less limited by this issue)

Daniel Stephens19:07:39

😂 cheers for the help


what’s the best way to ensure atomic channel writes? is wrapping it in an atom a good idea?


or should i connect it to a persistent queue and keep checking if something is available


what kind of channel?


putting side effecting operations inside an atom is the opposite of a good idea


k, figured


"atoms" ensure data consistency but do not lock, instead they run operations speculatively and retry if there was concurrent modification


like an async channel


as in clojure core.async?




if so, channel writes are already atomic (unless you mean something different than I do by atomic...)


yes, sorry - my writes are byte-level, and i’m thinking about ‘message’-level atomicity


maybe that’s just a bad idea to begin with


if the bytes need to go together, they probably shouldn't be mixed on the same channel as other bytes


or they should be tagged so they can be separated and separately consolidated


you can't block other users of a channel


(I mean, you can do it accidentally by clogging up all go blocks, but even then you haven't blocked usages of channels outside go blocks)


ok, yeah, i definitely need to rethink that. thanks for talking through it with me


one idiom:

 (loop [acc []]
   (let [x (get-from-place)]
     (if-not x
       (recur (conj acc x))))))


runs in its own thread, because IO inside go blocks just clogs things up and doesn't benefit from core.async


oh, cool, was not familiar with thread


but maybe you want to group every N bytes and send to a channel (use put! for that, pass in a channel when creating the thread)


there's a few variations, but that's a good template for getting IO data into core.async


@gtzogana also, since this is #beginners - consider that you can make everything a lot simpler and running (future (some-code that returns all contents)) and then calling deref later to get what it found


core.async makes coordination simpler, but often the even simpler thing is to not need coordination

Daniel Matz23:07:20

Hi, everyone. I'm working on migrating a hobby app I have on Google App Engine to Clojure/ClojureScript. I was able to deploy a simple web app by creating an uberjar with But when I try this with a more complicated app, I get a strange error that I don't understand:

Syntax error compiling at (clojure/tools/reader/reader_types.clj:1:1).
No namespace:
I can use the same uberjar to launch the app locally, this error only happens on App Engine. Also, I confirmed that clojure/tools/reader/impl/utils.clj is in the uberjar. Any ideas? Thanks!

Daniel Matz00:07:55

I've been struggling with this for a little while, but of course the moment I post it, I figure it out. I think I had insufficient memory on my App Engine instance. I tried adjusting the -Xmx and -Xss flags, but ended up just bumping up to an instance class with more memory, and things seem to be working.


Just curious, how much was the RAM before and after?

Daniel Matz00:07:17

I was originally using the free F1 instance, which has a 256 MB limit. I had set -Xmx128m -Xss256k, but I was getting stack overflows, so I bumped it up to -Xss512k. When I switched to an F2 instance, which has a 512 MB limit, it works, and seems to be using about 300 MB of RAM. Any tips for keeping memory usage down?


Don’t use many/any dependencies


Limiting stacks isn’t going to help much unless you have many threads


And 128m isn’t reasonable for a Clojure app in most cases - 256m is minimum; likely more if it’s something at least slightly serious (your app might work but you will pay a performance penalty because of limited memory)

Adrian Smith21:07:16

I imagine doing a native compile using graalvm would reduce the memory usage


That's better reserved for simple (command line) tools rather than a standard approach of doing things


Heroku free tier uses 512 container images. This is how I deploy a deps.edn project on Heroku free tier (via CircleCI)