Fork me on GitHub
#clojure
<
2017-09-21
>
ghadi01:09:48

@bbloom I've been working on a couple generator functions that I've found useful enough for Clojure, but one of the prior arts I'm looking at is Rust's unfold implementation. Lemme grab a link

Jim Rootham02:09:27

Is it really true that neither jetty nor httpkit gives you access to the HTTP verb (POST, OPTIONS) part of the request? Or am I just looking in the wrong places?

noisesmith02:09:08

use ring if you aren't already, and the request itself should have a :request-method key on it https://github.com/ring-clojure/ring/wiki/Concepts

noisesmith02:09:22

if you are using eg. compojure remember that the destructuring is hiding most of the data for you

Oliver George03:09:13

Is there a place for a #documentation channel to coordinate and support clojure documentation efforts?

noisesmith03:09:42

OK, link worked, that's a start

Jim Rootham03:09:27

@noisesmith There is indeed such a key, however when I get the value it is :options

Jim Rootham03:09:17

It looks very much like a bug where there is no value so the next key becomes the value.

seancorfield03:09:01

@jrootham Some browsers send an OPTIONS request first to find out what is supported.

seancorfield03:09:15

If you're building a REST API, you need to support OPTIONS.

Jim Rootham03:09:45

Ah, my bad, I was not expecting the leading :

Jim Rootham03:09:11

It is indeed an OPTIONS message.

seancorfield03:09:32

https://github.com/r0man/ring-cors -- we use that middleware to provide OPTIONS support, as I recall.

Jim Rootham03:09:25

I am working on building something exceedingly small and simple

Jim Rootham03:09:50

But yes, I need to support options.

Jim Rootham03:09:06

Even if not incredibly well

seancorfield03:09:30

Use appropriate middleware so you don't have to reinvent every wheel.

seancorfield03:09:19

Have you looked at ring-defaults? That offers a standard set of middleware for both "web sites" and "APIs" -- and SSL/non-SSL variants of each.

seancorfield03:09:08

(it doesn't include CORS so you still need that as well)

seancorfield03:09:21

Clojure is all about composing small things 🙂

Jim Rootham03:09:23

Thanks I will look at that, but CORS is the one thing I need and I don't want to have to use routes to use CORS middleware, which is what I inferred from the middleware documentation I saw.

seancorfield03:09:03

You inferred incorrectly. my-routes is just whatever handler you have.

noisesmith03:09:11

a CORS exchange needs to use OPTIONS right?

seancorfield03:09:19

Routing is just middleware anyway.

Jim Rootham03:09:10

@noisesmith Yes. I have to send a good header back when I get an OPTIONS.

Jim Rootham03:09:15

The docs are less than obvious. I don't even see where to find the type of my-routes

noisesmith03:09:55

a router is a function that gets a request and passes it to a request handling function

noisesmith03:09:11

usually it is made with a library like compojure or bidi

Jim Rootham03:09:33

What's the output?

noisesmith03:09:35

I mean technically it could just be a function that takes a request and does whatever but in practice that's what it is

noisesmith03:09:46

the output is described, along with the inputs, on the wiki page I linked

noisesmith03:09:16

if you return a string, that becomes the body of the response, if you return a hash-map various keys set properties of the response, it's all documented on the ring wiki

Jim Rootham03:09:42

Sorry for being obtuse, I will go quietly to my corner and RTFM.

seancorfield03:09:44

I'd recommend reading through the Ring documentation and following the tutorials to get a sense of how web development is done in Clojure -- it's very different to how most languages do it.

noisesmith03:09:22

@jrootham if you have specific questions I'm happy to help, but that wiki is more concise and accurate than I could manage to be if your question is just "what's a response" (actually you asked what the output was, but the handler should return a valid response etc.)

Jim Rootham03:09:46

I do have the mild problem of starting to learn Clojure on Monday and going live on Friday.

noisesmith03:09:55

oh wow that sounds super fun

Jim Rootham03:09:46

@noisesmith Thanks very much, I am mostly just being slightly confused by new nomclemature.

Jim Rootham03:09:48

The good part is that it is not a job, so the comeback isn't really painful, it's just an app for a small group.

seancorfield03:09:12

@jrootham In that case you want to write as little "boilerplate" yourself and rely on existing components as much as possible. After all, you shouldn't have to worry about writing code to support CORS -- but your app needs to support it -- so... middleware. Same with route handling, JSON decoding of the body (if relevant) and pretty much all the basic stuff any web app or API needs.

Jim Rootham03:09:14

But learning things in a hurry is actually fun.

seancorfield03:09:38

The downside is: you need to "grok" Ring -- and pretty much everything in Clojure is based on Ring or is compatible with Ring.

Jim Rootham03:09:50

CORS I need, architecture is 1 endpoint.

seancorfield03:09:22

Our web apps and REST APIs all use a standard stack of middleware and can choose to run on Jetty or http-kit at startup.

noisesmith03:09:42

there's a cors middleware, the model of ring is data -> function -> data, so you get a hash-map, do some work, and return something (likely a hash-map)

noisesmith03:09:56

and a middleware takes a handler and returns a new one with a feature added

Jim Rootham03:09:03

That makes sense

noisesmith03:09:06

which either adds data to the incoming request, or to your reply

noisesmith03:09:36

(usually - they can just do side effects instead of course)

Jim Rootham03:09:02

What, do they launch nukes? 🙂

noisesmith03:09:39

so the wrap-cors middleware will have some way of letting your handler pretend it's a normal request, and doing the options handshake with the client for you, and it should be smart enough to know when to pass data to your actual function

seancorfield03:09:19

Authorization? Middleware. Request logging or timing / metrics? Middleware. Providing configuration from the environment or database connections or something? Middleware. 🙂

noisesmith03:09:10

a pattern I use quite a bit is to decompose an app into features, and have each one manage its resources or configuration or initialization as needed, and return functions that others can use, including middleware for the http server. so the http server is the component that pulls in all the others, and knows which middleware to pull out of them and apply to its handler to get the runtime features it needs

Jim Rootham04:09:59

What do I put in the dependencies in project.clj to get ring-cors? And more generally how do I find that out?

dpsutton04:09:18

its on the github page for it. also, you can search clojars at https://clojars.org/

dpsutton04:09:45

the image is nice as its updated by publishing mechanism but you can't copy paste unfortunately

dominicm06:09:11

Fun fact, you can if you load the svg directly. Just not via github's proxy which converts it to png

pbaille06:09:00

is there an easy way to print functions as code instead of #object[...]?

pbaille06:09:43

I like to work with maps containing several little functions and it would be really usefull

dominicm07:09:34

@pbaille not one I've heard of.

pbaille07:09:45

I find it weird that functions being so opaque in a functional language

dominicm07:09:57

@pbaille the function as a value is fairly opaque, yep. I'm not sure why metadata isn't applied to functions, and is instead on their vars (or why not both!).

dominicm07:09:30

I'm sure there's a good reason though, usually surfacing around complexity or edge-cases which don't make sense.

miikka07:09:23

Is there greenkeeper-like service for Clojure, by the way?

reborg09:09:15

@pbaille (clojure.repl/source ns/fname) will fetch sources, assuming you can load them from a clj file sitting in the classpath

pbaille09:09:35

@reborg thank you, yes I was aware of this, but my concern is more about anonymous functions

pbaille09:09:57

and I think this stuff is related to vars

reborg09:09:27

once the lambda is evaluated there is no more sources available (unless of some tricky stuff to rebuild them from the bytecode)

pbaille09:09:21

yes I understand

misha10:09:21

@mbertheau don't actually use it, -> implies something should be done with 1st argument ({} in this case), and might waste reader's attention on figuring out whether author forgot to add something

misha10:09:21

generally, you would not want to see in code today something you called a "trick" 2 weeks ago opieop

ingesol11:09:39

Trying to embed a REPL in my app in code. Trying to run this in REPL: (clojure.core.server/start-server {:name "repl-server" :port 5555 :accept 'clojure.core.server/repl}) and then doing lein repl :connect localhost:5555. My windows command shell just hangs doing nothing. If I use a different accept-function, I get an error so the connection is established, it's just not starting the REPL. Is this the right way to start a new socket repl in code?

thheller11:09:39

@ingesol lein repl tries to connect to an nREPL server, clojure.core.server is a socket REPL which is just plain tcp.

thheller11:09:05

either start https://github.com/clojure/tools.nrepl or use a different client

ingesol11:09:47

Right, so I need to use something like telnet instead?

thheller11:09:00

that would work yes

ingesol11:09:25

Great, that got me one step further, thanks!

razum2um11:09:41

Anyone knows who’s done https://clojurians-log.clojureverse.org ? Are there any more friendly export formats beyond html?

plexus15:09:16

@U04V1HS2L there are the raw logs. They're not public but I can share them. Please DM me with what you need it for.

noisesmith11:09:13

@pbaille if you define an anonymous function with a name, it becomes slightly less opaque (`(fn foo [] ...)` instead of (fn [])) - the bigger gain is that stack traces make more sense, but it also becomes helpful if you run into a function literal. But! if you find yourself passing around maps full of functions, double check that your design wouldn't work out better with multimethods, or records implementing protocols.

pbaille11:09:16

@noisesmith thank you for the advice, I tend to avoid protocols and multimethod these days (I like to program with only maps and functions as much as possible) but they are really cool and when performance is needed I use them.

noisesmith11:09:55

@pbaille but this isn't about performance, it's about not making a total mess

noisesmith11:09:35

records are maps, protocol methods and multimethods are functions

pbaille11:09:51

maps of functions are more dynamic than records or types

pbaille11:09:11

I like to generate and compose those kind of things

noisesmith11:09:38

a clojure record is a superset of a hash-map - it does all the things a hash-map does, plus it lets you extend functionality in an organized way

pbaille11:09:51

yes I know

pbaille11:09:06

but how should I use a record where I can use a map

pbaille11:09:26

I don't need a type for dispatch

noisesmith11:09:01

because it lets you write code that makes a design clear to the reader - but if your function composition isn't organized around something that matches dispatch, then it's not your solution sure

pbaille11:09:42

yes it is organised around something that make dispatch

noisesmith11:09:45

@pbaille this suggestion came from working with a legacy codebase that was full of partials and first class functions that rendered the design of the system opaque, which over time led to errors and duplicated work

pbaille11:09:02

yes I see your point

pbaille11:09:56

I find this kind of way of coding offers a great deal of flexibility when used in the context of research and early dev but in prod maybe not

noisesmith11:09:08

the fact that the authors of the code could no longer read about and understand the parts of the system (or even see a system in order to recognize parts) led to an avalanche of partials and closures that became unmaintainable, the solution was to use records and protocols to deliniate the "big picture" features of the app. This is all slightly fuzzy stuff, if you don't need to do this you don't, that's fine.

noisesmith11:09:41

@pbaille right, the early flexibility sabotaged long term maintenance (in this project, and some other similar ones)

noisesmith11:09:03

well, flexibility plus lack of developer skill / lack of developer understanding

pbaille11:09:04

Yes I see, I wrote lots of protocols and types in the past, and I became tired of this layer

noisesmith11:09:21

right - I'm definitely not suggesting types and protocols everywhere

pbaille11:09:29

just maps and I feel good 🙂

pbaille11:09:42

but yeah I understand I think

noisesmith11:09:04

I thought I was seeing one of those classic symptoms - a big bag of functions in a hash-map and you lose track of what's what, and got flashbacks 😆

noisesmith11:09:18

For example, there are places in the code where I ended up replacing a hash-map with a record that implemented no protocols. This was so that a) we could actually determine how much of the garbage sticking around in the profiled system actually came from this source (someone had a suspicion and we had to measure it somehow) and b) later the fact that it was a #some.ns.foo{} clarified things somewhat when looking at the data that comes into the other part of the system

noisesmith11:09:28

I don't know if you have profiled a big clojure system, but the fact that everything is a clojure.lang.PeristentFoo datastructure or a LazySeq or clojure.lang.IFn eliminates 80% of the info a profiler would usually be giving you

pbaille11:09:23

I haven't profiled large systems neither in java neither in clojure.

pbaille11:09:31

I understand your point

mdrago102612:09:32

That’s a good point. I previously worked on a large clojure backed webapp and we used AppDynamics. It helped but we were definitely limited because of all of the clojure-specific structures

noisesmith12:09:48

it's not that they are clojure specific (it's all java at the bottom and the profiler can sort it all out), it's that they are not differentiated - the type of the data doesn't tell you anything about who made it other than "something in clojure" and the methods being called don't tell you much more than "some clojure generated byte code is running" (though in the latter case it's mostly noise rather than lack of data and if you keep digging you can actually figure out what code is doing what)

dbushenko12:09:29

does anyone have any experience with integrant? how do you find it comparing to luminus?

noisesmith12:09:33

integrant is a resource management library, luminus is a template made of a large number of libraries, the library it uses which integrant would/could replace is mount

noisesmith12:09:08

there's a lot of debate around mount vs. integrant vs. stuartsierra/component

noisesmith12:09:54

component was the first one, and people found it very useful for keeping their code running as they reload and redefine things (it ensures that even if you have running state you can safely tear it down and restart without exiting your repl process)

noisesmith12:09:22

but people found it either "too OO" or "too boiler plate" and alternatives like mount and integrant were made

noisesmith12:09:51

mount uses vars to put state in global shared mutable containers

noisesmith12:09:41

integrant uses function args to pass in components (like component does) but has a syntax and some conventions which simplify the common use cases

dbushenko13:09:17

sorry for confusion, I wanted to say 'duct' vs luminus, but thanks for explanation about integrant!

timgilbert16:09:59

Say, what do people favor as a convention for the names of constant values? I have (def ^:const buffer-size 100) and I'm wondering whether to name it BUFFER-SIZE, but that seems uncommon. I've also seen *buffer-size* to indicate globalness, but the convention in clojure seems to favor using *var-name* for dynamic vars, not necessarily global ones

noisesmith16:09:16

@timgilbert typically style guides suggest having no special convention for constants, all data should be constant in clojure with very few exceptions

timgilbert16:09:28

I'm generally ok with that, but there is something nice about a visual indicator that means I don't have to read back through a bunch of let-bindings to figure out where the thing was defined. Something about CAPS_LOCK just doesn't look right in Clojure though

noisesmith16:09:53

OK - you asked about conventions, and in clojure the "convention" is to not to name constants differently

noisesmith16:09:01

in practice something is either defined in a let block (the whole let block should fit in your screen at once) or else it's at the top level of the file, so finding it should not take long at all

dominicm16:09:21

There is a lisp convention which is +var+, but it's not considered idiomatic clojure.

dominicm16:09:35

https://dev.clojure.org/display/community/Library+Coding+Standards > Use earmuffs only for things intended for rebinding. Don't use a special notation for constants; everything is assumed a constant unless specified otherwise.

qqq17:09:14

1. How do I convert string (representing a unicode char) to a char? 2. The answer is NOT read-string, as it fails on "(" 3. "a" -> \a, "b" -> \b, "\u2323" -> \u2323 How can I do this in both clj/cljs ?

chris17:09:16

apply char works, but surely there's a better way

chris17:09:32

(apply char "\u2323")

thheller17:09:49

@qqq cljs doesn’t have a char type, for clj (.charAt "a" 0)

chris17:09:01

cljs doesn't have a char type, but it treats strings of length 1 as chars, right?

chris17:09:24

so (char "\u2323") outputs "⌣"

potetm17:09:30

@qqq assuming you want unicode code points (which is not the same as a Java Character), your best option is to use (.codePoints "adsf")

potetm17:09:46

unicode characters in Java are stupid tricky to get right because Java chars are UTF-16, which means that higher code points are represented with TWO chars

potetm17:09:04

And easy way to turn them into a seq is: (iterator-seq (.iterator (.codePoints "asdf")))

potetm17:09:53

Here's a good example:

(seq "𐐷")
=> (\? \?)
(iterator-seq (.iterator (.codePoints "𐐷")))
=> (66615)

potetm17:09:34

Notice just calling seq produces TWO java chars, when, in reality, they represent a single unicode code point.

potetm17:09:47

I have no idea about cljs. Surely there is some JS library that can translate a string to unicode code points.

josh.freckleton18:09:00

I'm using component and for some reason, no combination of the following is running my latest changes - component/stop - clojure.tools.namespace.repl/refresh (or (refresh :after ...)) - component/start any idea what's up?

donaldball18:09:59

You may want to ask in the #component room but I suspect the answer is that your code changes are directly part of your system components and you’re not rebuilding your system

josh.freckleton18:09:05

> ask in #component good advice, I will > directly part of system components they're actually not in this case 😞 > not rebuilding your system stopping and starting should rebuild my components right? even if I were changing components directly?

donaldball18:09:04

No, you’d just be calling the stop and start methods on the existing system

josh.freckleton18:09:54

(IE, if I add a prn command, or delete something, or add any new logic, and then refresh, I'm still working with my previous version until I close cider and relaunch it)

joelsanchez18:09:40

Hi, does anyone know how can I create a record with dynamic fields?

joelsanchez18:09:57

Right now I'm doing (defrecord MyRecord [attributes])

joelsanchez18:09:09

but then I can't assoc without prepending attributes

joelsanchez18:09:25

(assoc-in myrecord [:attributes :key] value)

tbaldridge18:09:44

you can assoc anything into a record

tbaldridge18:09:11

so (assoc myrecord :key value) will work

joelsanchez18:09:16

sure, but then the new field won't overwrite the old one, passed at construction

bronsa18:09:42

map/record fields are keyed nominally not positionally

tbaldridge18:09:32

There's also (map->MyRecord {}). that function is generated automatically for every record.

joelsanchez18:09:36

user=> (defrecord MyRecord [attributes])
user.MyRecord
user=> (def a (->MyRecord {:key :value}))
#'user/a
user=> (assoc a :key :new-value)
#user.MyRecord{:attributes {:key :value}, :key :new-value}
user=>

hiredman18:09:54

a record with dynamic fields is a map

bronsa18:09:13

@joelsanchez that's because you're creating a record that has the {:key :value} map as value for the :attributes key

noisesmith18:09:33

@joelsanchez wouldn't it do what you want if you used map->MyRecord instead of ->MyRecord ?

bfabry18:09:39

ya, a MyRecord can't not have an :attributes key, that would mean it doesn't satisfy the one and only requirement that makes it a MyRecord

noisesmith18:09:42

why put everything under one key?

bronsa18:09:48

use map->MyRecord instead

hiredman18:09:02

why use a record at all for dynamic keys?

tbaldridge18:09:12

I agree with @noisesmith use multiple keys at the same level as attributes

joelsanchez18:09:20

user=> (def a (map->MyRecord {:key :value}))
#'user/a
user=> (assoc a :key :new-value)
#user.MyRecord{:attributes nil, :key :new-value}

joelsanchez18:09:52

I can simply define no arguments and then use map->

bronsa18:09:53

yes but also note @hiredman's point, avoid creating named records just for the sake of it, use a map instead unless necessary

bfabry18:09:26

honestly unless you've got a specific reason (with data!) I wouldn't use records, just maps

tbaldridge18:09:39

and with maps you can use namespaced kws: {:attribute/key 42 :padding/left 33}

joelsanchez18:09:51

this is a very simple example outside of my application so that the issue would be clear

joelsanchez18:09:12

these records extend protocols and I need the polymorphism

joelsanchez18:09:03

(previously I was using maps and an awful lot of multimethods)

tbaldridge18:09:13

that's the most flexible

tbaldridge18:09:34

and the cleanest approach. Multimethods are faster than most people expect.

hiredman18:09:24

I dunno, I definitely prefer protocols, but if you have all dynamic fields, that is a map, not a record

hiredman18:09:42

but it is unlikely that you really have dynamic fields

hiredman18:09:10

you most likely have four or five sets of static fields that you are treating as the same thing for some reason

joelsanchez19:09:09

no, I don't, I have records whose particular fields I don't care about, but I care about what they implement and the type they have

joelsanchez19:09:33

the given solution works flawlessly

joelsanchez19:09:19

of course I could just use maps with a :type field, and then use multimethods: I was doing that before

joelsanchez19:09:31

not a fan of doing defmethod many times over 9 namespaces for defining how certain entities should behave

tbaldridge19:09:00

how does using protocols help with that?

tbaldridge19:09:12

you still have to define 9 protocol implementations

joelsanchez19:09:27

the defmethod approach ignores the fact that the defmethods that dispatch on the same type, are actually describing the same entity

joelsanchez19:09:02

yes, I still do, but I find it is way cleaner and less error prone

tbaldridge19:09:59

How so? (defmethod my-method :box [...]) Vs. (extend-protocol IMyMethod Box (my-method [...])

joelsanchez19:09:33

i'm not using extend-protocol, rather the contrary: extend-type (which is what defrecord does)

tbaldridge19:09:12

they're both syntactic sugar over (extend...) so I still don't understand

bronsa19:09:16

extend-protocol and extend-type are just synctactic sugar over extend

bronsa19:09:24

they're the same thing with different syntax

joelsanchez19:09:09

(defmethod method-a :box ...)
(defmethod method-b :box ...)
(defmethod method-c :box ...)
vs
(defrecord Box []
   WhateverProtocol
   (method-a [this] ...)
   (method-b [this] ...)
   (method-c [this] ...))

joelsanchez19:09:19

matter of taste but latter is more comfortable imo

bronsa19:09:20

i guess your pointn is that you like being able to group implementaiton for the same type together?

tbaldridge19:09:57

using protocols with defrecord are not closed for modification (or at least not for extension).

tbaldridge19:09:55

I get the point, and I've felt the same way in the past (grouping things). But I've more often been hit by the limitations of type polymorphism. That's where multi methods help.

joelsanchez19:09:21

what's the limitation? I'm very sure I will only dispatch on type

joelsanchez19:09:57

otherwise being equivalent (both can be extended afterwards, with more multimethods in one case and with extend-type in the other), I prefer this one in this case

joelsanchez19:09:28

also I get satisfies?

joelsanchez19:09:58

the multimethod approach gives me the equivalent of instance? by checking the value of type

joelsanchez19:09:21

to implement satisfies? I would need a dedicated multimethod

tbaldridge19:09:10

The limitations come when you want to dispatch on two types at once, or perhaps two aspects of the same type

joelsanchez19:09:20

sure, I won't

noisesmith19:09:20

@joelsanchez there's also get-method and you can check if the method returned is equal to the default

joelsanchez19:09:55

right @noisesmithbut these records may not extend any of the possible fns of the protocol, and still should be considered part of it

tbaldridge19:09:22

I've just found myself coming full circle lately. Started with multimaps, moved to protocols because of some of the reasons you listed, and over time found them more trouble than they were worth.

joelsanchez19:09:43

that sounds very possible 😛

qqq19:09:38

@thheller @chris @potetm: I ended up using (.charAt ...) -- thanks for all the suggestions!

aptr32220:09:12

I wonder how different libraries dependencies are resolved. For example I use 2 libraries and they use different version of the same third one. I bugs me a little but not that much to explore myself 🙂

dominicm20:09:24

The algorithm it's a little complex. But the software that answers that question is a java library called maven/aether-resolver

aptr32221:09:36

Do you know if it's overwritable? like in project.clj

seancorfield21:09:32

Yes, you can specify :exclusions on a dependency to prevent the third one being pulled in, then specify it as a dependency directly with the version you want.

seancorfield21:09:24

[[lib-a "1.2.3" :exclusions [lib-c]]
 [lib-b "4.5.6" :exclusions [lib-c]]
 [lib-c "2.2.2"]]

aptr32221:09:42

aha! thank you

danielcompton21:09:10

Also, I think any explicit dependencies you provide at a top level will override any transitive dependencies, so you don't need the exclusions, but it does make things clearer, and prevents lein warnings

aptr32221:09:18

yeah, that the easiest.

aptr32221:09:27

how I wonder how to find out what libraries are used. I built uberjar and looked inside, that's kinda gross

aptr32221:09:52

is there 'normal' way?

seancorfield21:09:19

lein deps :tree

seancorfield21:09:43

(or boot show -d, if you're using Boot)

seancorfield21:09:52

Leiningen has a "pedantic" mode that makes sure you have no possible conflicting versions (and won't run if you do!).

seancorfield21:09:12

Boot also has a "pedantic" mode that shows possible conflicts (but still runs). We have a small Boot task that throws an exception if it detects conflicts -- and we use that for all our builds!

aptr32222:09:26

ha! it does and it shows plenty of conflicts for me. bucket of worms

seancorfield22:09:40

It can be really painful to get to zero conflicts -- but it's worth it for your sanity! And once you get there, it's easy to stay there even when adding new libraries.

aptr32222:09:16

lein deps :tree prints to stderr. surprise

Garrett Hopper21:09:28

@ghadi Generated css class names

ghadi21:09:40

can you say more?

Garrett Hopper21:09:48

Yeah, sorry. I'm using the hash of the styles to generate css class names. The main issue is with server side rendering creating different class names. Up to this point I thought they were equivalent, until I ran into integers not being consistent.

ghadi21:09:01

Are the styles a 1-level map?

Garrett Hopper21:09:20

Normally, but not always

Garrett Hopper21:09:37

They might be any immutable type.

Garrett Hopper21:09:47

Perhaps an md5 hash or something.

ghadi21:09:14

well if it's not too complex, you may want to flatten the map and sort explicitly, then do some sort of securehash

ghadi21:09:41

flattening is harder when it's nested more than one level deep

Garrett Hopper21:09:57

That may work. The goal is really just to get a consistent unique name.

ghadi21:09:32

clj/cljs 's hash is not secure, and you almost certainly will have collisions

noisesmith21:09:58

clj and cljs would generate the same bytes from transit for equal data right? what about hashing the transit string?

Garrett Hopper21:09:19

That's not a bad idea. ^

ghadi21:09:24

no @noisesmith -- transit does not have a canonical encoding

bfabry21:09:30

do they generate the same bytes for an unordered map? that would be surprising

noisesmith21:09:31

oh, that's too bad

noisesmith21:09:00

@bfabry if that was the only difference you could just sort the map entries as part of the process, but yeah, good point

Garrett Hopper21:09:04

@ghadi So far it seemed to not have any conflict issues. Though I haven't used it extensively. I'm just now realizing integers are different.

ghadi21:09:06

you have to sort map's key-values, transit doesn't give you a hook into that (there's also caching)

Garrett Hopper21:09:25

Is pr-str consistent?

bfabry21:09:36

I'd do as ghadi said. turn the data structure into nested vectors (where they're sorted in the case of a map) then sha1 it or whatever

bfabry21:09:42

pr-str is not consistent

Garrett Hopper21:09:26

Even for basic data types?

ghadi21:09:54

when printing a map, ordering is not guaranteed

tbaldridge00:09:34

What are you trying to do? Arachne has a lib for consistent equality hashing

tbaldridge00:09:49

i.e. things that are equal in Clojure will have equal serialization hashings

tbaldridge00:09:54

let me look up the lib

ghadi00:09:32

ahhhh I was searching for ObjectHash

ghadi00:09:54

i would like to have something like valuehash but for serialization

ghadi00:09:09

got any other bright ideas @U07TDTQNL?

ghadi00:09:20

i'm using a blockchain with a requirement that the state transition functions produce the same hashed output, and the output could be an arbitrary blob. if you serialize JSON as the output of a function that runs on every blockchain node ('validator') they must all produce the same result -- so you have to sort json keys

martinklepsch21:09:34

How do you access this inside a proxy definition?

bfabry21:09:04

(pr-str {:1 1 :2 2 :3 3})
=> "{:1 1, :2 2, :3 3}"
(pr-str (assoc {:3 3} :2 2 :1 1))
=> "{:3 3, :2 2, :1 1}"

noisesmith21:09:30

@martinklepsch every method takes a this arg

noisesmith21:09:00

oh - wait, it's automatically named 'this

bronsa21:09:29

yeah proxy is anaphoric

gonewest81821:09:31

So, a discussion I’m having with @staypufd over in the #docker channel has led to what I believe is a bug in tools.nrepl. Is there someone here who can review what we’re seeing and confirm if a Jira ought to be opened?

bfabry21:09:35

I think you only need to xform+sort map and set and you're good though. particularly if you're just talking about css which is probably a small subset of types

staypufd21:09:29

@gonewest818 I tried it again last night and it does seem that you are right. I haven’t fully verified yet, but it does seem if you don’t put “0.0.0.0” for :bind that you get a Socket failure on the nrepl

gonewest81821:09:11

Good. I was pretty sure. I’ve got a repl in alpine linux right now and it’s easy to reproduce that unhandled exception by following what “start-server” does under the hood.

staypufd21:09:49

Yes, exactly what I ended up doing as well.

staypufd21:09:04

Even in my Luminus project, it did that

staypufd21:09:33

I have to pass in “0.0.0.0” as the nrepl :bind or it fails with that Socket exception every time

staypufd21:09:00

Thanks so much for the help yesterday too.

staypufd21:09:13

@gonewest818 It would be good for someone else to see same issue. We’re seeing it on DockerMac, so wonder if that is the issue as well, b/c DockerMac docs say there are a few issues surrounding network stuff…

hiredman22:09:23

you should try adding -Djava.net.preferIPv4Stack=true to you java command line

gonewest81822:09:08

btw, I just tried this and it doesn’t resolve the issue.

hiredman22:09:17

some operating systems prefer to resolve localhost to ::1 instead of 127.0.0.1

bfabry22:09:35

iirc ::1 is the ipv6 loopback address?

gonewest81822:09:09

It’s Alpine linux where (I believe) ipv6 is disabled by default. The description in NREPL-83 suggests it will try “::” first and then fall back to “localhost” but in this case the attempt to bind “::” throws an exception and the fallback never occurs. It’s easy to workaround but IMHO the feature is not behaving as advertised because of the unhandled exception. Steps to reproduce are in this gist: https://gist.github.com/gonewest818/ae5c5caf49380f81c5a2667408da6d4f

hiredman22:09:32

open an issue then

hiredman22:09:10

you should check what version of nrepl you are running first though

hiredman22:09:47

yeah, the nrepl change isn't in a released version yet

gonewest81822:09:49

yep. nrepl 0.2.13… the gist is very explicit

hiredman22:09:32

oh, actually the nrepl readme is out of date

hiredman22:09:44

(it only mentions 0.2.12)

staypufd22:09:16

I filed a PR last night on that

gonewest81822:09:03

Ok, well I’ve googled a bit and it appears the proper path for opening a bug is to ask in the Google group first, and confirm if the behavior is in fact considered a bug.

the2bears22:09:35

Hi All, I've inherited some code that has some threads running a a service, distilled to essentially this:

(def test-channel (chan 5))                                                                                                                                                 

(def running (atom true))

(thread
  (while @running
    (let [m (<!! test-channel)
          x  (m :a)]
      (some-fn x)))

(defn stop-service []
  (close! test-channel)
  (reset! running nil))

noisesmith22:09:27

that's going to hit an npe when <!! returns nil from a closed channel

the2bears22:09:46

Okay, trying to edit it so it looks like 'code'

the2bears22:09:52

hold on 🙂

noisesmith22:09:58

you can put '```' on each end

the2bears22:09:24

Thanks! And yes, there's an NPE

the2bears22:09:00

Which really only happens during testing, as that's about the only time the service is stopped as such. However, there has to be a nicer idiom/pattern to doing this?

noisesmith22:09:50

yes

(loop []
  (when-some [m (<!! test-channel)]
    (m :a)
    (recur)))

the2bears22:09:50

It's not causing issues in production, but another error in the tests masked this NPE

noisesmith22:09:00

then you don't need an atom - closing the channel stops the loop

the2bears22:09:20

Hmmm... yeah, I like that. I guess my code snippet doesn't exactly show the issue as in the 'let' expression there are more bindings based on the read from the channel

noisesmith22:09:49

then put a let block inside when-some

noisesmith22:09:00

since you don't want to do any of that if you got nil on the channel anyway

the2bears22:09:36

Makes sense, much cleaner. I'll try it out, thanks!

the2bears23:09:02

@noisesmith Thanks for the clarity and help, took your suggestions and the code is so much better!