Fork me on GitHub
#clojure
<
2017-03-30
>
qqq00:03:33

if I have a webapp where users uploada pdf and it's converted to jpegs and I process the jpegs; am I better off doing this in clojure in java server side or in js using pdf.js clientside?

joshjones01:03:21

after you process the jpegs, do you then store them, validate them somehow, or something else? @qqq

joshjones01:03:57

one consideration is transit size -- which is cheaper/easier to send? one pdf, or several jpegs? you would tend to process client side if the post-conversion jpegs would be much smaller to send over the network

mattgeb01:03:10

@qqq Not sure if this is directly helpful for your project, but just wanted to mention I'm actually working on something similar but with a different approach. Using Clojure as a Code Generator for ImageMagick commands to turn pdfs into pngs.

qqq02:03:18

@joshjones @mattgeb : the XY problem is: I'm writing some custom code for doing OCR in a case where tesseract-OCR doesn't work that well

qqq02:03:39

the input is scanned PDFs, I need to convert it to jpegs before processing

qqq02:03:49

I'm slightly worrieda bout malicious PDFs, but not too worried

joshjones02:03:04

if the size of the pdf roughly equals the cumulative size of the jpegs, i'd say go with whichever method is easier to implement robustly (client side conversion + multiple files sent, or server side conversion + one file sent). client side conversion would save network in the event there is an error in conversion. i'm not up on pdf security though, so do your homework on that one for sure

didibus04:03:50

@ag (and (f x) x) except it'll return false instead of nil when not true

didibus04:03:32

@ag nevermind, just read further down, topic exhausted

qqq05:03:02

why doesn't clojure have an dissoc-in, which is (dissoc-in obj path blah) ... and after doing the dissoc, it walks up the path, and kills all the empty maps

moizsj06:03:04

do you know of a good clojure lib that allows maintaining a list of entries that have a ttl (and also a max size)? i could use ttl-cache-factory but the expiration only kicks in on a write to the cache. i need the expired entries to not be present when i enumerate the entries in the list (or map). of course i could hack around it by making a faux write each time i enumerate the entries. but any better ideas anyone?

quan06:03:02

there is a clj wrapper for java expiringmap lib https://github.com/luminus-framework/expiring-map

moizsj06:03:54

@quan thanks! does it allow specifying a max size? or is that via using interop on the underlying java object?

quan06:03:23

i don't think clj wrapper lib has, but original java lib support it https://github.com/jhalterman/expiringmap

lsenta07:03:21

Is there a particular protocol to use when defining resources to be used with with-open ?

lsenta07:03:10

Also, is there anything else that resemble Python contextmanager in Clojure?

thheller07:03:54

@lsenta java.lang.AutoCloseable or any .close method really (`java.io.Closeable` works as well)

lsenta08:03:57

Thanks Thheller, I still miss a detail on how to use these,

lsenta08:03:31

I have a function (make-something) and (clear-something X)

thheller08:03:40

uhm they are java interfaces not protocols

thheller08:03:28

(defrecord Something []
  java.lang.AutoCloseable
  (close [this] ...))

thheller08:03:10

or deftype depending on what make-something creates

lsenta08:03:02

Yea that's where I'm not sure what's the clojure way to do it, I use a library that gives me a "store", I don't particularly want to dig into their implementation details

lsenta08:03:30

All I want to do is to have a way to make sure the store is closed when I'm done with it

lsenta08:03:13

coming from Python my reflex is to build something that'd look like

with my_store() as s:
  do something
implicitly, my_store would yield the instance and close it at the end

thheller08:03:15

hmm if you don't control the store it will be hard to attach the close to it

thheller08:03:33

(let [x (make-something)] (try (do-with-something x) (finally (clear-something x))) maybe

lsenta08:03:04

That's more or less the with-open macro 😅

thheller08:03:04

I personally don't like cleanup in finally but it ensures the clear-something is called

thheller08:03:15

yeah I know 🙂

thheller08:03:58

the problem usually is an exception in close .. usually nothing you can do about that but still

thheller08:03:41

with-open is built with a .close method in mind which is a java idiom which most java things implement

lsenta08:03:52

So my only choice is to write a macro, every time I have a (start) (stop x) to call

thheller08:03:01

some clojure things as well but if the lib you are using doesn't you are out of luck

thheller08:03:12

well you don't really need a macro for that but yes every different impl requires a new solution

thheller08:03:50

https://github.com/stuartsierra/component and others try to solve this issue by introducing a common protocol

thheller08:03:11

which I dislike personally due to the invasive nature of the protocol but still

yonatanel08:03:25

Is anyone using clojure for system programming? Maybe with a specialized runtime?

lsenta08:03:29

@thheller I haven't thought of going straight to these, that could help, thanks.

yonatanel08:03:50

@thheller How is the Lifecycle protocol invasive? Perhaps you will like Integrant? https://github.com/weavejester/integrant

thheller08:03:16

@yonatanel IMHO a "thing" (component, service, whatever you want to call it) should not be coupled to the container it is running in

thheller08:03:00

which you do if you have the component implement a thing of the container

thheller08:03:00

not that component does that as you can implement the protocol independently but some things do

thheller08:03:22

so if a lib depends on component that is a warning sign for me

thheller08:03:29

not sure about integrant ... never used it. not sure I like the multi-method approach

yonatanel08:03:08

@thheller A more data-oriented approach would be to tell the lib which function to run using a symbol. Is that what you mean?

thheller08:03:09

I have this as a tiny little helper utility, basically the same integrant does but in code not data

thheller08:03:13

{:something
 {:depends-on [:something-else]
  :start something/start
  :stop something/stop}}

thheller08:03:57

something/start will be called as (something/start instance-of-something-else) and (something/stop whatever-start-returned)

thheller08:03:38

really not better or worse than integrant or component, solves the same problem with different tradeoffs

thheller08:03:39

you can't really make this completely data oriented IMHO, so whether you organize things into multimethods or a protocol or a clojure map is up to personal preference really

Shantanu Kumar11:03:49

@thheller There’s also (vaguely similar to your idea) https://github.com/plumatic/plumbing#graph-the-functional-swiss-army-knife and (forgive my shameless plug) https://github.com/kumarshantanu/dime — I’d love to know any other ideas to solve this kind of problem

thheller12:03:21

@kumarshantanu yeah there are at least 5 other solutions I know of and probably 50 other in Java. I used to work on PicoContainer in Java 10+ years ago and still have no idea what the best approach is. Something about the simple declarative nature of clojure maps appeals to me as everything is in one place but other solutions (like yours) attempt to also free you from passing arguments to functions. I don't mind that part so my solution doesn't cover it at all.

aspra12:03:38

Hi all, is anyone aware of a lein tool to clean up dead code ? I only found https://github.com/venantius/yagni but I have some issues with it. It also seems a bit outdated

jstew12:03:48

@aspra - Have you looked at eastwood? It does a lot more than yagni, but it does also check for unused code.

aspra12:03:30

@jstew I just saw that it has linters for it. Thanks a lot!

jstew13:03:24

I do also use yagni with boot-check and it works fine, even though it's dependencies are older. I haven't had any issues with it, really.

jstew13:03:58

I suppose it's one of the nice things about boot, running things with older deps in pods, where it won't affect the rest of the system.

aspra13:03:58

I have never heard or used boot before. Thanks for the tip

aspra13:03:42

@jstew quick update: eastwood doesn’t seem to do what I want. A lot of nice stuff but not unused defns

jstew13:03:22

Ah, unused locals only? That's what I was afraid of.

aspra13:03:20

yeah. Thats an interesting approach I suppose http://diogo149.github.io/2014/08/30/dead-code-clojure/

tolitius13:03:55

@jstew > I do also use yagni with boot-check and it works fine, even though it's dependencies are older boot-check uses latest releases:

venantius/yagni "0.1.4"
jonase/kibit "0.1.3"
jonase/eastwood "0.2.3"
lein-bikeshed "0.4.1"
which older dependencies do you mean? (and you are correct all checks are run in boot pods that use these deps)

jstew13:03:43

@tolitius - yagni's dependencies are out of date (according to github anyway), not boot-check's. boot-check has always been a pleasure to use.

tolitius13:03:38

@jstew ah.. got it. thanks for the feedback 🙂

aspra14:03:45

@jstew this works https://gist.github.com/joelittlejohn/4729776. Maybe quick and dirty but does the job

venantius14:03:06

(yagni author) I’d welcome PRs to update deps, etc 😉

jstew14:03:54

@aspra Quick and dirty, yes... Looks like it will work though. Would have to be modified for cljc and cljs is the only immediate issue I would have with it.

aspra14:03:33

@venantius thanks! In my case I have some dependency issues it seems. I think I have more of an issue in my project that with yagni. Just saw that it was not updated for years and it assumed it is not maintained. Sorry 🙂 If I find some issue in particular I will let you know

jstew14:03:26

Semi related, many clojure libraries look "abandoned" but are really just mature and feature complete. Clojure and most of it's 3rd party libraries have great backward-compatibility which eliminates the need to keep updating code when clojure or a dependency is updated.

aspra14:03:51

true, good point. But you know how it goes: it is not my code so must be the library 🙂

octopuscabbage14:03:23

@jstew what about clojure makes that true?

lvh14:03:49

Hello! Is there such a thing as a lazy classloader? Something that downloads a jar and puts it on the classpath at the last minute?

lvh14:03:05

(You know, from maven central or something.)

lvh14:03:15

I appreciate that I could, uh, ship a zip file and maybe run lein deps on it

lvh14:03:20

Context: lambda.

lvh14:03:27

octopuscabbage huh, good idea; I was considering lein in fact

lvh14:03:56

more context: I’m running lambda, there’s a 50MB jar limit, they don’t ship Java AWS SDKs, all java AWS SDKs together is >50MB

lvh14:03:03

so I was hoping I can do it from the JVM maybe

octopuscabbage14:03:24

we dynamically load clojure files

octopuscabbage14:03:35

but i’m not famiilar with how to do it with jars

gonewest81814:03:04

@jstew However solid test coverage isn't always available. In that case difficult for a developer to confirm compatibility without writing their own tests (ideally sending a PR to the library) or more often the case taking a leap of faith. I had exactly this kind of question this week, finally asked the library maintainer because there was no other way to know a priori. And the library is complex enough that I'm in no position to write tests for it. The maintainer replied "it should work, we're making it compatible with newer versions of [that dependency] without modifying the code".

octopuscabbage15:03:50

something here about a hack to load jars onto classpath https://groups.google.com/forum/#!topic/clojure/AJXqbpGMQw4

octopuscabbage15:03:41

URLClassLoader.addURL internally it seems

octopuscabbage15:03:09

use an http client to download the jar to some cache and then use one of those methods to load it

jstew15:03:18

@octopuscabbage @gonewest818 - That's very true. Test coverage is the only way to prove compatibility, and often poor tests are created. I speak from my own experiences compared to say, Ruby and node.js.

lvh15:03:27

@octopuscabbage yep, looks good. Thanks!

rplevy16:03:26

If anyone is interested in going back to IRC but found client tools to be not as good as Slack: I recently found an app called Riot. It integrates a lot of different chat protocols into one experience and it actually succeeds in being a great user experience, IMO.

rplevy16:03:37

I've been following Clojure IRC again because of it and it's more active than last I checked.

seancorfield16:03:12

Any follow-up on the Slack vs X debate should happen in #community-development -- not in this channel /admin-hat-on

qqq17:03:35

*.cljc is still considered a GOOD THING right? One particularly frustrating thing is having to use conditionals for cljs.reader/read-string vs clojure.core/read-string Is there any plans of creating a "standard cljc library" where a the conditionals are hidden away, and it exports a common set of functions useable in both clojure and cljs ?

bcbradley18:03:21

i think i recall reading somewhere that the way clojure's lazy sequences work is that they are realized ahead of time in small batches (like 32 elements at a time)

bcbradley18:03:25

is that sentiment correct?

Drew Verlee18:03:34

Clojure West videos out already, awesome!

bcbradley18:03:16

if so, is there a way to ensure that elements are only figured as late as possible?

jstew18:03:46

@bcbradley would https://clojuredocs.org/clojure.core/delay be an option for what you're trying to do?

fmnoise18:03:51

@drewverlee where I can find em?

bcbradley18:03:03

maybe it would help if i explained what i'm trying to do

dpsutton18:03:13

i don't think delay is what he wants. ie, (take 2 (map get-id-from-database [1 2 3 4 5])) would ideally be only 2 database calls. but lazy in clojure doesn't necessarily work like that if i understand correctly

bcbradley18:03:41

i've been working for some time on a game engine in clojure and i'm really hoping to elevate the model above the conventional way game engines are traditionally built

bcbradley18:03:52

i want to treat the sequence of game states as an infinite lazy sequence

bcbradley18:03:05

thanks thats probably what i wanted!

bcbradley18:03:30

i need to find some resources that really explain how clojure chunks its lazy seqs somewhere

jstew18:03:58

It's funny, I was just reading about chunking and creating lazy seqs in joy of clojure on the bus this morning.

jr18:03:05

what does chunking have to do with lazy seqs?

bcbradley18:03:26

to my knowledge a lazy sequence in clojure isn't necessarily as lazy as possible

bcbradley18:03:50

many operations which return a lazy seq and take a lazy seq will realize more elements than necessary (buffering up)

bcbradley18:03:06

this is presumably because doing so is more computationally efficient for the most common cases where the elements are very small

bcbradley18:03:21

its undesirable at times though

jr18:03:41

I think it depends on the seq you're operating on

bcbradley18:03:10

i didn't know that

bcbradley18:03:34

how does it know the difference between something that is expensive versus something that is cheap?

jr18:03:46

it depends on the producer

jr18:03:06

to take fogus' example:

(def gimme #(do (print \.) %))

#'user/gimme
user=> user=> (take 1 (map gimme (range 32)))
(................................0)
user=> (first (map gimme (range 32)))
................................0
user=> (first (map gimme (iterate inc 0)))
.0

bcbradley18:03:33

i would have expected the last example to output a single .

jr18:03:02

it prints the . and val

jr18:03:15

if a lazy sequence is built up via lazy-seq then it is not chunked

bcbradley18:03:29

alright thankyou for your help! thats what i wanted to hear!

jstew18:03:41

(chunked-seq? (range 100)) and (chunked-seq? (iterate inc 0)) produces true and false respectively.

jr18:03:57

range is special for performance reasons IIRC

jstew18:03:03

I suppose if you're in doubt you could always check with chunked-seq?

jr18:03:41

partition uses lazy-seq internally so this will work:

user=> (first (map gimme (partition 2 [1 2 3 4 5 6 7 8])))
.(1 2)

bcbradley18:03:38

tell me if you think this approach is sound: (iterate (partial time-travel n) [initial-events initial-states]) => (target-events target-states)

bcbradley18:03:04

time-travel is a function that takes an int and a vector of [events states]

bcbradley18:03:20

if n is 1, it just marches the game state forward one frame, if it is -1 it rewinds

bcbradley18:03:30

you could do integers other than 1 and -1 of course

bcbradley18:03:52

i'm still experimenting so idk if my ideas are sound yet; if you've got any advice i welcome it

jr18:03:10

as long as you don't hold onto the head then I think you're fine with that approach

bcbradley18:03:44

i want to provide different kinds of time-travel

bcbradley18:03:02

time traveling without rewinding the events (so mouse movements and such don't get replayed)

bcbradley18:03:17

and full time-traveling, rewinding events and all, so the game replays itself

octopuscabbage18:03:50

you’d have to be careful with side effects

bcbradley18:03:23

yeah i'm hoping that i can rely on clojure's default immutability to give me a solid base of operations to work with so i don't have to use side effects

jr18:03:08

I would probably store the result of the transitioned state in a vector

jr18:03:20

that way it is indexed and doesn't include side effects

bcbradley18:03:24

i was thinking about that too

bcbradley18:03:35

it would make rewinding much easier too i think

bcbradley18:03:43

i wouldn't have to make each operation invertible

qqq18:03:16

I know that Compojure exists. I'm trying to learn Ring by doing basic stuff by hand. I'm looking at https://github.com/ring-clojure/ring/wiki . Where is the section for handling GET/POST requests? (I really want to do this by hand to learn how Ring works; please don't sugest a framework 🙂 ).

jr18:03:05

that's what I did for a snake game

jr18:03:25

store the frame offset and make each frame available in an indexed coll

jr18:03:07

dec the frame offset for rewinding

bcbradley18:03:17

hrm, that seems like a solid approach

bcbradley18:03:45

if the state is stored in a vector, and the next state is always conj on the end of it

bcbradley18:03:00

i'd extend that approach to events in a similar way-- there would be an event vector

bcbradley18:03:11

that way I can time travel on one, the other, or both

bcbradley18:03:37

that makes macros trivial to implement, makes prince of persia type time travel easy, and it also makes "real" replays easy too

octopuscabbage18:03:47

you’re going to have to use side effects eventually, you can usually facade them into being idempotent though

octopuscabbage18:03:55

for example writing stuff to disk

bcbradley18:03:23

i was hoping to just memory map the game state to the disk

bcbradley18:03:27

and avoid that altogether

octopuscabbage18:03:49

that’s just an example, you should figure out a of a strategy now that fits in your framework

bcbradley18:03:06

yeah i'm just trying to consider this from a fresh point of view

bcbradley18:03:14

i know how i would do it in c, or java

bcbradley18:03:26

but clojure gives me a lot of powerful tools i don't have in those languages

bcbradley18:03:31

i'm trying to think of how to leverage them

rmuslimov18:03:35

Can you please recommend libs/tools to build SOAP server from scratch on clojure. I’m looking to clj-soap, but seems like it no longer maintained.

bcbradley19:03:53

why is clojure.lang.PersistentQueue not a part of the core library?

bcbradley19:03:32

where is the documentation?

jr19:03:35

it's a data structure

jr19:03:00

works like a seq. what documentation do you desire?

bcbradley19:03:19

idk, there are some set operations that aren't supported by any other structure, like union and intersection

bcbradley19:03:28

there are some map operations that aren't supported by other ops

jr19:03:34

think of a queue as a list

bcbradley19:03:36

vectors have efficient nth behavior

jr19:03:52

you can conj and pop but no random access

bcbradley19:03:03

ok, so no efficient nth

bcbradley19:03:24

this is what i desire in documentation

bcbradley19:03:39

there are all kinds of ways to implement a queue, it'd be nice to have an explanation

bcbradley19:03:03

c++'s deque DOES support random access for instance

bcbradley19:03:15

and it satisfies the interface for a queue

bcbradley19:03:42

i can understand not having a reader literal syntax for every data structure out there

bcbradley19:03:46

but queues seem pretty basic

bcbradley19:03:08

#[a b c] would be something i would expect

jr19:03:26

#queue is a supported tag in transit IIRC

jstew19:03:53

I was expecting a reader literal as well. Not a deal breaker for me though, it works as a Queue, like I expected it to.

jr19:03:47

hmm nvm that's a data tag for cljs

bcbradley19:03:56

why isn't there a clojure wrapper function for it?

bcbradley19:03:12

like hash-map for instance

bcbradley19:03:54

it feels like it is "there" but "discouraged"

jstew19:03:00

honestly, I do not know. I've seen it asked on the google group and stackoverflow and I don't recall any sort of definitive answer.

bcbradley19:03:04

i can't think of any other reason to keep queues hidden like this

jr19:03:22

what is the queue use case?

jr19:03:36

conj to the end and pop the front?

bcbradley19:03:01

i can't keep every game state alive, so i have to "forget" some of them

bcbradley19:03:08

seems like an ideal use of queues

jr19:03:10

I think you want a vector for that

bcbradley19:03:21

isn't popping from the front of a vector slow?

bcbradley19:03:32

or should i do subvec?

bcbradley19:03:44

hrm ok, that works for me

bcbradley19:03:49

will it forget the head and all that?

jr19:03:14

not if you have a reference to the head

jr19:03:25

the game state should just contain the vector value

bcbradley19:03:39

after thinking about it a bit this is basically what i have:

bcbradley19:03:53

(future time) => more-time

bcbradley19:03:00

(past time) => less-time

bcbradley19:03:11

(present time) => state

bcbradley19:03:34

time, more-time, and less-time are just vectors like you describe

bcbradley19:03:44

present basically gets the last element of the vector

bcbradley19:03:56

future marches the game engine to the next state, so its expensive

bcbradley19:03:14

past just pops the last element off

bcbradley19:03:42

the main game loop is defined as (iterate future initial-time)

bcbradley19:03:51

the way i see it every game is just a succession of states

jr19:03:55

vectors aren't lazy so they don't have the head reference issue

bcbradley19:03:22

even games that give the illusion of time-travel are really still just a succession of states

bcbradley19:03:42

it just so happens that some of those states look like prior states (the time-traveling bit)

bcbradley19:03:14

in other words, if you view (future time) as (time-travel 1 time) then "time travel" in games like prince of persia are actually time-travel WITHIN time-travel

bcbradley19:03:25

atleast thats what i've come up with

bcbradley19:03:28

@jr thanks for the advice, it helps

jr19:03:49

np good luck with your game engine

qqq20:03:53

does anyone have a minimal example of: restful service client = cljs + cljs-ajax + transit server = clj + ring + transit I've played with ring; what I don't get is "how do I return an clj object in json/transit encoded" ?

jr20:03:17

you have to read the transit data by detecting the content-type header

qqq20:03:46

@jr: sorry, I don't understand what you mean

jr20:03:58

for the server

qqq20:03:58

yeah, I have ring-middleware-format installed as adependency

qqq20:03:02

the problem is, there's no sample code on that page

qqq20:03:08

I don't know how ot put those pieces togehter

qqq20:03:27

copying/pasting taht should make it work

jr20:03:16

don't copy and paste

jr20:03:27

do you understand ring middleware?

jr20:03:21

wrap-restful-response :formats [:transit-json] is probably the middleware you want

jr20:03:09

it will encode your :body response as transit-json and add the header Content-Type: application/transit+json

jr20:03:45

you will then need to decode that transit response on the client to get cljs data

sveri20:03:08

cljs-ajax does that automatically and uses transit as default

qqq20:03:40

@jr: I generally prefer to (1) get something working first, then (2) try playing with working example + reading docs

qqq20:03:50

plain doc reading == I can't see how all the pieces fit together

qqq21:03:15

on client side, I do cljs-ajax/POST ... on the server side, I get a ring request, the :body field has an object of type org.eclipse.jetty.server.HttpInputOverHTTP how do I extract the clojure data of the POST from this HttpInputOverHTTP ?

qqq21:03:59

okay, got it all working, so server side is: #(transit/read (transit/reader ... :json))