Fork me on GitHub
#clojure
<
2018-01-26
>
pgarrett00:01:32

I'm working in a project that makes liberal use of futures, and it's hard to tell at a glance which functions return futures and which return immediate values. Can anybody share conventions they've used to solve this problem?

pgarrett00:01:15

I was considering suffixing the future function names with &, with the mnemonic being it's like running something in the background in the unix shell

greglook01:01:29

Hmm, interesting approach - the troll in me immediately thought of using an ellipsis for the same purpose, so you’d have (do-foo... x y)

greglook01:01:16

But three characters is probably a lot for that. & is unusual but seems like it could be a nice marker if it was internally consistent in a code base.

noisesmith01:01:15

dots in names are a problem - perhaps three in a row would avoid the issues but I’d be cautious

noisesmith01:01:12

for example

penguin.tracking-test=> (defn foo... [] :OK)
#'penguin.tracking-test/foo...
penguin.tracking-test=> (foo...)

CompilerException java.lang.ClassNotFoundException: foo//, compiling:(/private/var/folders/xb/gyyjxv511q7d9lr24s9_1rzc0000gr/T/form-init7128136753223600421.clj:1:1)

noisesmith01:01:24

because it thinks you are invoking a constructor because of the . before the )

noisesmith01:01:01

that error message makes me think it is doing a string/split on . and then combinging with slashes to make a class name - that would make sense I guess

greglook01:01:24

ah, yeah I can see how dots on the end would be an issue

arrdem02:01:53

Is there a better way to write a spec for a constant than a singleton set?

arrdem04:01:03

Okay. Glad to hear this is a pattern of sorts.

slipset06:01:11

WRT the Clojure survey, one of the things I find very important is the stability. Upgrading to newer versions of Clojure is basically a non-event.

seancorfield08:01:32

@slipset This is how we went to production back in 2011 on a 1.3 alpha build and we've taken pretty much every alpha and beta build to production over the years -- so we can always be using the very latest new features in Clojure in production!

bostonaholic17:01:08

I heard great things about your clojure upgrades and it’s amazing. But @U04V70XH6, I’m sure it’s not all been rainbows and unicorns. I’d love to hear about something that went wrong with an alpha/beta build and what you had to do to resolve it.

seancorfield17:01:39

We dodged a bullet with 1.5.0 — we were on a prerelease build in production, 1.5.0 came out with a newly introduced memory leak and our deployment schedule meant we didn’t put it in production because 1.5.1 came out so soon. That would have been bad — but it wasn’t a prerelease build 🙂 The worst thing I can think of is that we have to change our code as new core functions appear and because we test against master-SNAPSHOT (as well as whatever fixed build we’re using), that means our build can break overnight without us touching anything.

seancorfield17:01:27

We were very early adopters of clojure.spec (and this is sort of why we follow prerelease builds — to get access to new functionality) and that meant we had a fair bit of churn as that evolved (namespace changes, functions removed, lots of functions added).

seancorfield17:01:08

But, no, there’s really nothing that “went wrong with an alpha/beta build”… the Clojure/core team do an amazing job in that regard!

Alex Miller (Clojure team)17:01:10

hey Sean, we haven’t actually done an announcement about it as there’s not really anything to announce yet, but 1.10.0-alpha2 is out already :)

seancorfield17:01:15

Haha… yeah, I saw the pushback reader commits and was surprised you’d released not one but two alphas already just for that! 🙂 We’ve been testing against 1.10.0-master-SNAPSHOT for a while but there’s nothing in the alpha(s) to cause us to move yet.

Alex Miller (Clojure team)17:01:30

it’s almost like there’s an unreleased project relying on those new features that needs them

seancorfield17:01:28

Yeah... almost... and maybe that'll inspire us to jump to an alpha again 🙂

carocad11:01:30

hey guys, quick question. I am trying to use an agent as a component in stuartsierra/component framework but it throws saying that Agents cannot be cast to iObj. I checked the source code and there is indeed a cast from Agent to iObj in order to change the metadata. Is this a known issue? is it not possible to associate meta data to an agent?

noisesmith17:01:20

can you elaborate why you’d use an agent rather than a record or hash-map? sometimes I’ll put a mutable inside a record or map but I don’t grok how a mutable object as a component would work (or why I would do that)

carocad18:01:51

@noisesmith because I can 🙂. Seriously, the only point of using an agent was not having to go through the verbosity of creating a record and implementing the protocols when I know before hand that only one value will be inside. The same thing as having a component be an atom.

noisesmith18:01:26

that is why it accepts a hash-map - why not a hash-map with an atom under a key?

carocad18:01:40

component supports that use case by using identity as start/stop and since I refresh the system for that, then there is no problem with it

carocad18:01:09

@noisesmith as I said, verbosity. Yeah I could do it like that, but I just didnt want to, and since component says it support elements where start/stop is a no-op then I wanted to use it

noisesmith18:01:55

you need to be able to assoc onto a component, that is non-negotiable

noisesmith18:01:01

you can’t assoc onto an atom or agent

noisesmith18:01:42

it supports a hash map, then it just uses identity, but if you can’t assoc the other components it uses onto it as keys, component breaks, that’s fundamental to the design

noisesmith18:01:33

as a degenerate case you could make a deftype that allows assoc as a no-op and contains the agent in a field, but I’m not sure if that’s better than using a hash-map

carocad20:01:41

> you need to be able to assoc onto a component, that is non-negotiable I dont think that is correct. You only need to assoc into it if the component depends on other components. If the atom/agent is suppose to hold the mutable state of the system or part of it then there shouldnt be any problem in just declaring it as a component. For me putting everything into a hashmap/record is the same mentality as everything must be an object … but at that point we are back to java 😞 I already got a very good explanation on another thread of why it doesnt work but yeah I know that what I was asking is not the usual pattern

noisesmith21:01:54

The comparison I would use here is that clojure.core/+ and clojure.core/min (and most other mathematically useful functions) are designed not to be overloaded to non-numeric types. This isn’t because “everything is a number” it’s because we don’t do polymorphism for its own sake. I don’t find the idea of a system where some components are maps with keys representing their features alongside things they depend on, and others are arbitrary types, to be a coherent design. And there’s every indication that the author of the library thinks similarly.

carocad23:01:20

@noisesmith from the component readme > Any type of object, not just maps and records, can be a component if it has no lifecycle and no dependencies. For example, you could put a bare Atom or core.async Channel in the system map where other components can depend on it. I think mixing different types is at the core of Clojure. Not allowing that would be a waste in my opinion

noisesmith23:01:12

Oh, I didn't catch that. Still seems odd to me, but good to know.

stathissideris15:01:18

is there anything tha combines core.match pattern matching with the extensibility of multimethods?

greglook16:01:39

@carocad agents implement IRef and alter-meta!, since they are stateful. IObj is for immutable values, so it’s a different interface.

carocad18:01:37

thanks for the explanation, but this is not obvious to me. I thought that the agent itself is immutable but that the value that it points to is what changes :thinking_face:

carocad18:01:46

maybe I am giving this much though …

carocad18:01:11

I worked around it but it is still unclear to me 😞

greglook19:01:57

An agent is a stable identity for a piece of state - the state inside is immutable, but that makes the atom itself a mutable representation. When you change metadata on a normal clojure value, you get back a new value with the updated metadata. Ref types, on the other hand, are meant to be stable so it wouldn’t make sense for you to get back a different agent when you change the metadata. Hence, the side-effecting alter-meta! which updates it in-place.

greglook19:01:31

One common pattern in component systems when you are working with stateful objects that may not play nice with metadata is to wrap them in a map if you don’t need lifecycle methods. If you do, then usually you’d define a new record type which had that state (say, your agent) as a field and implements component/Lifecycle and the start and stop methods.

carocad20:01:19

@U8XJ15DTK thanks a lot for your thoughtful explanation. It is much clearer now 🙂 I wanted to use the agent precisely to avoid having to wrap my mutable representation in a record since for me that is kind of make everything an object mentality which starts to sound absurd taken to that point.

sova-soars-the-sora17:01:45

IPFS support in clojure is: complete & good?

greglook19:01:40

@sova you might be interested in https://github.com/greglook/blocks and https://github.com/greglook/merkledag-core - IPFS-inspired, and semantically compatible if you wanted to back a block-store with IPFS itself.

sova-soars-the-sora20:01:30

Wow thanks for the links! I'm very interested in this. Our application leverages a blockchain and also IPFS for distributed storage like torrents... the design decisions of today make the towers of tomorrow...

greglook20:01:41

if you like those, I’m building an immutable analytics database on top of merkledag as a POC: https://github.com/greglook/merkle-db

ghadi17:01:13

can use the java library @sova

andy.fingerhut17:01:38

InterPlanetary File System?

sova-soars-the-sora17:01:11

Java library does work but looks like i'm trying to floss my teeth with code

sova-soars-the-sora17:01:06

IPFS is, based on my research, the next level up from BitTorrent for distributed file storage, distributed apps, etc. Provided we can make a nice wrapper for it... I think a lot of people would be drawn to making Distributed Applications in Clojure 😄

sova-soars-the-sora17:01:44

anyway, attack me with an update if you use IPFS 😄

rickmoynihan17:01:28

@stathissideris you want to look into predicate dispatch… IIRC Rich was banging on about it for a long time and then David Nolen took up the challenge and started exploring the area a few years back, but I’m not sure if he ever shipped anything.

stathissideris18:01:52

@rickmoynihan I think it was core.match that was supposed to fill that gap

stathissideris18:01:29

@noisesmith it sounds like he’s opposed to pattern matching that’s “closed, enumerated in a single construct”

noisesmith18:01:32

of course nothing stops you from using a cond form as your defmulti dispatch function, I do that frequently

noisesmith18:01:55

but that undermines the intention of defmulti because cases are hard wired in the dispatch

stathissideris18:01:21

what I’m asking for it something that would be in the spirit of multimethods but with more powerful dispatch

noisesmith18:01:30

the problem is that if your dispatch is pattern matching, you can’t extend the dispatch without extending your pattern, right?

noisesmith18:01:15

@stathissideris that sounds interesting but I don’t fully understand it - I didn’t bring up that point from rich to discount your idea, just to provide context for why things are the way they are now

stathissideris18:01:56

ok, but what I’m saying is that he’s opposed to pattern matching which is localised and final

noisesmith18:01:30

right, and that’s the part that is intriguing though I don’t understand it personally - I have no idea what extensible pattern matching looks like

stathissideris18:01:26

there seems to be a talk by David Nolen on the subject: https://www.youtube.com/watch?v=iCl9rB1tyxo

rickmoynihan18:01:46

@stathissideris, @noisesmith is right about RH being opposed to it on principle. Predicate Dispatch is like extensible multimethods + pattern matching. That’s why I was suggesting you look into it; though the last time I looked (which was sometime ago) David Nolen hadn’t released anything. So I assume it never left the research or toy prototype stage.

rickmoynihan18:01:27

though I think the research it’s based on is complete; I think the open questions are around doing it in an open manner.

rickmoynihan18:01:47

That talk is probably one of a few he gave where he mentions it

stathissideris18:01:17

it would be really convenient to have such a mechanism for overriding/extending parts of my javafx DSL

stathissideris18:01:44

I’ll look into pdfn (linked above)

noisesmith19:01:42

I am starting to imagine a multimethod that uses another multimethod for dispatch - (defmulti dispatch-foo (partial map type)) (defmulti foo dispatch-foo) - so you extend both dispatch-foo and foo… maybe… maybe that’s crackpot

arrdem19:01:06

I mean, it’d work… it’d also be fairly hard to reason about.

noisesmith19:01:59

yeah, so worthless

joelsanchez19:01:21

best tool for documenting a websocket API? swagger can't handle it and traditional documentation libraries are focused on functions only

noisesmith19:01:24

like I said before I have no solid idea of what sane and clear extensible dispatch would look like

joelsanchez19:01:07

alternatively I can build my own 🙂

noisesmith19:01:13

@joelsanchez what about making a spec or schema that succeeds for valid messages?

joelsanchez19:01:43

@noisesmith sure, easy to do that, but I was talking about the frontend side of things

noisesmith19:01:18

you can easily use spec or schema in frontend code - that’s a common pattern in my app actually to use the same schema file on both ends

noisesmith19:01:41

unless your frontend isn’t clojurescript

joelsanchez19:01:42

yes, but for example swagger lists the requests you can make, the params they take, and lets you try them

noisesmith19:01:51

well, schemas are data

joelsanchez19:01:08

good idea, thanks

PB19:01:10

Does min not work on a collection of instants?

noisesmith19:01:31

clojure numeric functions are not generic for things that are not numbers

noisesmith19:01:02

@petr that said you can use eg. min-key or (first (sort …))

PB19:01:16

Yep, I wanted to do something as such ((juxt min max) [...]))

noisesmith19:01:36

=> (min-key int \a \b)
\a

noisesmith19:01:52

(apply (juxt min-key max-key) #(.getTime %) ...)

PB19:01:10

Yeah I saw that min needs to be applied to a coll

PB19:01:17

Thanks!

ikitommi19:01:26

@joelsanchez we have used swagger for web-socket api docs too. Actually generate a set of http post endpoints & ws-message handlers from a plain map (or multimethods).

joelsanchez19:01:54

could you show me an example?

ikitommi19:01:54

Not right now. need to extract it from a project. Goal is to push all to a small support lib.

ikitommi19:01:27

but it's a combo of spec-tools, schema-tools, ring-swagger, reitit & eines.

sova-soars-the-sora20:01:30

Wow thanks for the links! I'm very interested in this. Our application leverages a blockchain and also IPFS for distributed storage like torrents... the design decisions of today make the towers of tomorrow...

Jim Rootham20:01:37

I have an uberjar using jetty that runs fine locally. However when I deploy it to heroku it dies with a null pointer exception in ring_debug_logging. Is this a known issue with a (hopefully) simple fix? Also, I inserted logging into my ring stack and I see that, but I do not see any output from println calls in the heroku logs.

delaguardo21:01:51

Looks like your heroku box doesn't have allocated tty. You can try to configure your logger to use file instead of stdout.

Jim Rootham03:01:51

Don't think so, I inserted an error into my program and got the println error message, so I am pretty sure it's not configuration.

ccann22:01:42

is it strange that it’s faster to read an SVG byte stream into a String and re-find the viewBox attribute than it is to use (-> (clojure.data.xml/parse (io/reader svg)) :attrs :viewBox)

hiredman22:01:07

xml is not a regular language, so by definition it requires something more powerful than a regex to parse it, plus it does whatever the java xml does, and then generating the clojure structures

hiredman22:01:19

it is not surprising at all