Fork me on GitHub
#clojure
<
2017-04-27
>
hiredman00:04:18

the last argument is the collection you are reducing over

hiredman00:04:45

the function (in this create-n) is run over each element of that collection looping the accumulator through

hiredman00:04:27

so the first element in your case is 1, which then create-n tries to destructure against [p acc]

hiredman00:04:00

you may just have the arguments to the accumulator function reversed

weavejester01:04:42

@bradford No, text and binary bodies are treated the same. The only thing that changes is the Content-Type header.

gonewest81801:04:07

Suppose I want to generate a ring response containing binary data of variable length, and I don’t care what the bytes are. I wrote an implementation like this

(let [s (-> (io/input-stream "/dev/urandom")
            (BoundedInputStream. size))]
  (-> (response s)
      (content-type "application/octet-stream")))
The body of the response map is the input-stream. Am I supposed to be closing the stream, or does ring take responsibility for that?

gonewest81802:04:14

Second question. I’ve wrapped this implementation in ring.middleware.logger, and I’m noticing log entries like

2017-04-26 13:49:37,453 [qtp370196540-16] INFO onelog.core b18d - Finished :get /file?size=1000000000 for 0:0:0:0:0:0:0:1 in (1 ms) Status: 200
or a gigabyte of data in 1 ms. I take it, the logging middleware is not waiting for the contents of the input-stream to be fully consumed. One millisecond must be the reported time merely for the response map to be constructed, right?

noisesmith03:04:35

@gonewest818 hitting the endpoint from localhost, an OS can just skip the network device and just hand over a pointer

noisesmith03:04:55

(or something close to it) - on linux with copy on write it's effectively this

wei04:04:58

are there any tricks for allowing changes to middleware wrappers to take effect without restarting the app?

noisesmith04:04:18

@wei one approach is to var quote your handler, allowing the server to see the new definition in the var when it is changed

weavejester04:04:04

@gonewest818 It’s closed for you when when stream is completely read from.

weavejester04:04:43

@gonewest818 And the logger probably logs when the request comes in; the stream remains unconsumed until you either read from it or close it.

wei05:04:53

@noisesmith thanks for the suggestion. is there something special about Ring that makes the var quote work, or is that just a property of the language? I’ve implemented my own wrapper system for a non-http event handler and the var quote isn’t giving the newly-evaled changes, though it might be something else I’m not accounting for

noisesmith15:04:57

when you use a var instead of just using a symbol, if your function captures the var (as in (start-server #'handler port)) it will look up the new value of the “handler” definition each time it is used (instead of capturing the value of the handler at the time the server is captured)

noisesmith15:04:42

if your event handler is created with def, and is passed as an argument to some service as a startup argument, this pattern will work, but there are definitely cases where this doesn’t really work. For a more full featured and resilient way to capture new definitions you could check out stuartsierra’s component (or another similar lib called integrant) which is made for fast restarting of stateful parts of a system without restarting your repl

matan06:04:51

anyone out here using anything that makes tests display more elegantly, or be written as browsable (html) outputs rather than terminal dumps? any recommendations?

matan07:04:15

well, I think I found this https://github.com/ruedigergad/test2junit, not too many github stars lol 🙂

igrishaev08:04:22

I had strange issues with test2junit. It absorbs exceptions sometimes but does not show them in XML report. So you see 1 error occurred at the end with an empty XML.

matan08:04:55

@igrishaev thanks I also see that the resulting html reports are pretty lame. I think I'll avoid the bother using test2junit, maybe something else pops up

matan08:04:12

I should check how that plugin works, and try to get tests output into my own code that will easily render them the way I like

thheller09:04:57

yeah I guess I answered my own question .. the type hint is ignored and the submit Runnable path is chosen instead of Callable. maybe a bug.

bronsa09:04:56

@thheller type hints are not used for reflective calls, correct

bronsa09:04:09

type hints are only used at compile-time, reflective calls happen at runtime when the type hints are not available anymore

borkdude11:04:43

Just for fun, is there a mechanism which lets me do this in Clojure?

(derive :message/html :message/default)
(get {:message/default "Hi"} :message/html) ;;=> "Hi"
So getting something out of a map, if the key doesn’t match, it tries the parent?

borkdude11:04:02

Without writing my own version of get

bronsa11:04:07

no, only isa? is hierarchies-aware and get uses =

curlyfry13:04:37

@borkdude You could probably write your own get-derived using ancestors

borkdude13:04:16

@curlyfry yeah, it’s pretty straightforward using parents and walking the ancestor tree

martinklepsch13:04:36

So I’m trying to nippy/thaw an input stream but I’m not sure how to convert it into something that implements java.io.DataInput — any pointers? 🙂

martinklepsch13:04:29

@igrishaev ah, guess I wasn’t quite clear. The input stream contains the previously frozen bytes.

martinklepsch13:04:47

But I think I found a solution

igrishaev13:04:52

@martinklepsch yes I see that answer above does not cover your question as well. I think before thaw for something, you should say to nippy what is that.

martinklepsch13:04:06

(-> (s3/get-object :bucket-name bucket
                     :key (str "some-thing"))
      :input-stream
      IOUtils/toByteArray
      nippy/thaw)
org.apache.commons.io.IOUtils to the rescue

martinklepsch13:04:56

My question wasn’t very well worded, sorry about that! 🙂

dergutemoritz13:04:56

@martinklepsch Shouldn't (java.io.DataInputStream. your-input-stream) do the trick?

martinklepsch13:04:44

@dergutemoritz I thought I tried that but let me try again

martinklepsch13:04:55

@dergutemoritz a yeah, got this one:

ExceptionInfo Unrecognized type id (78). Data frozen with newer Nippy version? 

dergutemoritz13:04:21

Huh pretty weird!

martinklepsch13:04:48

I think nippy needs to see the complete content before it can do anything so it cannot really process streams

martinklepsch13:04:58

(mostly guessing here admittedly)

dergutemoritz13:04:13

Hm that wouldn't be ideal

dergutemoritz13:04:21

@martinklepsch You used nippy/thaw-from-in! there, right?

dergutemoritz13:04:25

No, that should be fine. Looks like a bug if you ask me.

tbaldridge13:04:05

no, the original version of nippy wasn't written that way, and it supported streaming data.

dergutemoritz13:04:49

@tbaldridge Which statement is your "no" referring to exactly?

tbaldridge13:04:55

@martinklepsch did you write this data, or is it coming from another source

tbaldridge13:04:50

@dergutemoritz I was stating that when I originally wrote the protocol behind Nippy the protocol was simple: typeid, opaque-data-format-based-on-typeid, typeid, another-opaque-block

tbaldridge13:04:21

so two strings would be: 4,3,"foo",4,2,"hi"

tbaldridge13:04:31

where the second number was the length of the string.

dergutemoritz13:04:43

@tbaldridge Ah, right, so it should be streamable. Assumed as much, yeah 🙂 Didn't know you were the original designer, cool!

tbaldridge14:04:10

But things may have changed in the past 5 years, as I dropped the library (deep-freeze) and started using Fressian, and the code was forked (with permission) and became nippy.

dergutemoritz14:04:23

@martinklepsch Added a standalone repro case that works without S3 involved 🙂 Does it reflect what you're trying to do?

martinklepsch14:04:04

@dergutemoritz thanks. I think so, yes

dergutemoritz14:04:12

Aha, it works when freezing with freeze-to-out! instead

sveri14:04:11

Hi, are there opinions on using: https://github.com/adambard/failjure? I find it to be easier to work with when executing several side effecting functions that may fail. Are there different alternatives execept if / else / try / catch?

tbaldridge14:04:14

@sveri I had a problem, so I used monads, now I have two problems 😛

martinklepsch14:04:29

I still don’t know what a monad is (… but feel like I should)

tbaldridge14:04:49

returning errors as values isn't a bad approach, and neither is try/catch. The problem really comes down to the fact that errors create branches in your code. And this is a problem of managing branches.

sveri14:04:45

@tbaldridge I knew you would say something like this 😄 Exactly, and manging branches creates a lot of boilerplate code, some of which can be omitted by using failjure.

tbaldridge14:04:36

what can failjure do that exceptions can't? (perhaps with a few macros added)

sveri14:04:24

Like I said, I save some code, and it increases readabilitiy of which I am a big fan. And I dont have to write the macros myself.

qqq14:04:33

ha! I just wrote something similar, I have a monad "do-m", then every part returns either: [:error .... } OR [:ok value]

qqq14:04:10

@tbaldridge : I realize you disagree, but I find monads creates cleaner code.

qqq14:04:23

It separates "what does this one piece do" from "how does it fit in with the rest, i.e. the bind function"

tbaldridge14:04:10

cleaner code or not, it breaks the return types of your code. Assuming a get-state accessor for the state monad, what does (map get-state itms) return? It's not a list of states.

tbaldridge14:04:34

So monads pollute your type system, not unlike async code.

tbaldridge14:04:59

So that's when I sit back and ask myself "Clojure has been around for some time, surely I'm not the only one to have ever had this problem...what do other people do?"

qqq14:04:38

get-state :: object -> M a map get-state :: [object] -> [M a]

sveri14:04:42

@tbaldridge If I have not been clear about that, I am interested in what other people do.

qqq14:04:08

I was recently writing some code implementing a database, I endced up writing a StateT Either monad because: for most ops, I need it to either: (1) fail // short circuit rest of evaluation (2) modify the state, return some value

leonoel14:04:14

@qqq what is the benefit compared to exceptions ?

tbaldridge14:04:06

@sveri no, you have been clear. And I think you could build most of failjure off of exceptions. But that's the general idea, you have exceptions or values either one makes you deal with additional branches, just depends how you want to deal with it

qqq14:04:23

@leonel: one can certainly simulate the Either monad with exceptions; but there's still the "modify this state, return a new value" State Monad left

tbaldridge14:04:11

@qqq how's the performance of that, since you're putting it in database code that needs to be rather fast.

qqq14:04:19

@leonoel : oh, there's one more thing -- I was trying to type check this; type checkikng monads is much easier than dealing with type checking exceptions

qqq14:04:31

@tbaldridge : I haven't benchmarked it yet.

qqq14:04:46

this is all part of the "turing tarpit" attempt

dpsutton14:04:48

how are you type checking

qqq14:04:10

I'm annotating varaibles with ^ / meta data

qqq14:04:30

then I read in te *.clj file and run a STLC (currently very weak) type checker on it

qqq14:04:40

it's not spectrum or core.typed

qqq15:04:10

the idea is: I'll write plain cljc files, put code that is type check-able in a certain directory, annotate their types with metadata, then type check them

qqq15:04:21

code that my type checker is too weak to type check live in . different namespace

qqq15:04:39

it's all very hackish at the moment

tbaldridge15:04:49

Sounds like you're using the wrong language 🙂 Hate to break it to you, but if you want monads and types...Clojure probably isn't a good fit. I've heard of this language called Haskell though...

qqq15:04:34

@tbaldridge : suppose you're writing some code; half of it which will type chedk in Haskell, half of whch will hnot type check in Haskell; what do you do?

qqq15:04:46

you write the entire thing in clojure, put the two halves in differet namespaces, and run a type checker on half of it

tbaldridge15:04:57

Simple: I don't care about type checking. Not being sarcastic, but having code that's 90% pure, and dynamically typed gives me the best bang-for-the-buck.

tbaldridge15:04:35

Trying to make my code 100% pure, or statically typed, often just makes me spend more time fighting the system than actually addressing the problem at hand.

qqq15:04:05

I'm not going for 100% either; I'm only going for 50%

qqq15:04:13

I just like the 50% that can be type checked to be typ checked

Pablo Fernandez15:04:36

Using compojure, is it possible to have a section of the path that has two variables, as in /size/{width}x{height} ?

sveri15:04:17

So this is some code I have written lately: https://pastebin.com/jzMpBYyC In there lines 2, 3 and 4 throw either an exception or return some validation error. With failjure I have access to the specific error message and it stops executing the code after the error happened. Now when I imagine that I remove failjure I just see several try / catches that act on every specific error in a different way (bye choosing the appropriate error message). I am still interested if I just use try / catch wrong or of everybody goes with all the error handling?

tbaldridge15:04:34

@sveri don't you get the error message/info thing with ex-info and ex-data?

sveri15:04:34

@tbaldridge I never used these functions, thanks for the pointer, I will look into them

toddwannacode16:04:48

Hey all. I am a noob programmer, just learning the trade for seven months or so. So, my question is which is better for web development, in terms of handling large number of requests - elixir or clojure?

zane16:04:35

You would almost certainly be able to do just fine with either.

thheller16:04:07

it completely depends on what you are trying to do but either will do fine

toddwannacode16:04:22

Ah okay. Thanks.

fmnoise16:04:59

I would say it's the subject of taste, if you love lisp then choose clojure, if you prefer classical infix notation then elixir may be more suitable for you

qqq16:04:05

generally, if you reach the point your traffic dwarfs a single beefy machine, you can raise VC and hire cognitect to handle all your scaling problems 🙂

toddwannacode16:04:41

Actually, I am just asking theoretically, since I was trying to learn about concurrency.

piotr.owsiak16:04:24

Hi, is recur not allowed inside for?

qqq16:04:25

does datomic, or any db support efficient 2d-range queries, say: every element has a bbox and I want to find all entities whose bbox overlaps wigh given bbox

qqq16:04:32

(a bbox is a bouhnding box of: x1 x2 y1 y2)

ghadi16:04:15

@piotr.owsiak no because for isn't a loop, it's a lazy comprehension that ends up generating a lazy seq + lambda (fn [])

piotr.owsiak16:04:33

@ghadi so its because of this lambda which would get into the way of recur, right?

ghadi16:04:46

yeah the recur ends up in a different function

piotr.owsiak16:04:59

@ghadi if so, then it makes sense

ghadi16:04:36

np. that's the technical reason -- but what were you trying to achieve so we can solve that?

piotr.owsiak16:04:11

I'm working on 4Clojure to learn Clojure, specifically this problem: http://www.4clojure.com/problem/28#prob-title

piotr.owsiak16:04:44

but I think I'm close to a working solution

piotr.owsiak16:04:59

I'm trying to solve this without mutation

dpsutton16:04:17

can loops have empty "arity"? does this blow up because the recur has a different arity from the loop and luckily doesn't work or could you conceivably start jumping around in there?

piotr.owsiak16:04:02

@dpsutton good question, actually I have been thinking about good way of breaking from an inner loop in a way that would break from the outer at the same time without any mutable state, I'm just learning Clojure but this might turn into a blog post asking for an idiomatic way of doing that

joshjones16:04:46

@piotr.owsiak yeah, so one of the things hardest to do when learning clojure is to solve things without loops where you would use a loop in another language (not that a loop can't be used in your case)

joshjones16:04:19

but as stated, for isn't a looping construct anyway, so ... probably not the most appropriate tool

piotr.owsiak16:04:38

ok, so if anyone is interested in sharing an opinion, here's a "pattern" I would use in C#: https://gist.github.com/hiredgunhouse/6ae3bf6c0b4866c739b7d052464010cb

noisesmith17:04:04

@dpsutton you can’t recur across arities

noisesmith17:04:27

@piotr.owsiak there’s a function called reduced that returns a dereffable item with your data in it, and reduce will stop and return the value if you return a reduced from one of the steps - there’s a reduced? predicate that tells you whether something is a reduced

noisesmith17:04:18

so at least one approach would be to have enclosing loops check for a reduced and return it if found (and deref the reduced if it reaches the top level)

piotr.owsiak17:04:26

@noisesmith so you would opt for a mutability in such case?

noisesmith17:04:45

@piotr.owsiak I always opt out of mutability if possible - and yes, reduced means not mutating anything

noisesmith17:04:12

though another option, more similar to your example, is to use a delay and use realized? to see if it has been forced

noisesmith17:04:43

eg. (let [done (delay :OK)] (loop [] (when-not (realized? done) ….. (if foo (force done)) …))))

noisesmith17:04:03

unlike a mutable, a delay has exactly two states: realized, and unrealized, and can only change once

joshjones17:04:09

too complicated for this use case

piotr.owsiak17:04:24

@noisesmith awesome, I'm learning a lot 😄

noisesmith17:04:04

@piotr.owsiak and really, for your example, I would use neither reduced nor a delay - I would just put a conditional check around the call to recur

noisesmith17:04:19

since there is no implicit recursion, you can just not call recur and then you are done

joshjones17:04:32

delay, promise, force ... too much for a 15-line program which guesses numbers... @piotr.owsiak what you have is close to your C# example, minus the actual random number part

piotr.owsiak17:04:31

@joshjones yes, is that a good way to do it?

piotr.owsiak17:04:52

@noisesmith I have conditionals in my current solution, is that the way you would do it?

joshjones17:04:25

well, you never actually get the number that was guessed, or compare it to a randomly generated number ... so it doesn't do anything, but your approach is on the right track. it's a simple task, so keep it simple @piotr.owsiak

noisesmith17:04:26

yes - I apologize for mentioning reduced and delay before actually looking at your code, @joshjones is right, it’s too simple to need that stuff

piotr.owsiak17:04:28

@joshjones sure, the rest seems simple and obvious, I'm most concerned about the "pattern" of breaking out of two loops like that

noisesmith17:04:53

usually we avoid it by not explicitly looping

piotr.owsiak17:04:04

@noisesmith ok, thx, I'll look into reduced and delay though to learn more

noisesmith17:04:12

we use higher order constructs that let us think about the task at hand rather than the mechanics of repetition

piotr.owsiak17:04:57

that would make a higher order function then 🙂

ghadi17:04:32

see those ^ "generator" functions @piotr.owsiak

piotr.owsiak17:04:55

@ghadi is that in the std lib?

ghadi17:04:21

it's in every library I write tho

ghadi17:04:19

Any time I need to paginate an API:

(let [api (fn ([] (GET "/initial"))
	          ([response]
	            (GET (next-link response))))]
	(series api next-link))

piotr.owsiak17:04:43

looks like a usefull piece of code indeed

ghadi17:04:56

you could use supply for the other usecase you mentioned earlier

kaosko17:04:12

so this is much more a Dokku question but I think most Dokku users don't care about clojure/java. the herokuish' buildpack-clojure include java 7 - does anybody know how to make it use java 8?

byron-woodfork20:04:16

Question: How do you handle debugging things within your clojure projects?

souenzzo20:04:06

intellij step/stop interface(sometimes I use) and tons of "log/debug" (default debug )

byron-woodfork20:04:52

@souenzzo ah I see. So you rely on Intellij's debugger tool?

dpsutton20:04:11

CIDER also has a step through debugger

souenzzo20:04:13

Not sure, but it's intellij+cursive.

jimmy23:04:50

prn and log/debug

val_waeselynck07:04:25

You also can use a spy macro which logs the expression it wraps and its value, and also stores them in a stack for later inspection at the repl - e.g ,`(spied 2)` to inspect the 3rd most recently spied value.

byron-woodfork20:04:52

@souenzzo ah I see. So you rely on Intellij's debugger tool?

tjtolton20:04:27

help me out? I'm trying to essentially block derefing an atom until a value has been put there. Basically, I want to use an atom as a channel. I should probably use a channel, shouldn't i?

tjtolton20:04:09

but the nice thing about deref is you can give it an explicit timeout

dpsutton20:04:36

you can setup a timeout channel and use alts. you'll know if you received from the timeout channel or a value

tjtolton20:04:46

and a default value. I want that behavior... and if I set up all this bloody test structure to use alts and stuff just for some one off test code...

dpsutton20:04:33

what do you mean test structure. can you separate the logic from the "infrastructure" and just test the logic that you need?

dpsutton20:04:45

i'm guessing you weren't going to be testing the timeout on derefing a var

tjtolton20:04:53

yeah, that's really heavy weight for this one off test code though @dpsutton. I'm just verifying when something has been put on a queue in a test, and I don't want to just poll for it repeatedly

tjtolton20:04:14

the test is to call an http endpoint and verify that, as a result of calling it, something got put on a queue.

tjtolton20:04:35

and I can register a callback for the moment it got put on a queue.

tjtolton20:04:06

I'm basically having the callback reset a "is something on the queue yet?" atom to true

dpsutton20:04:24

is this a real http call or mocked for a test?

tjtolton20:04:37

and so basically, I could just poll the state of that atom on a loop

tjtolton20:04:54

but id prefer to just wait with a timeout until some value has been put there

tjtolton20:04:06

its a real http call.

dpsutton20:04:14

in a unit test?

tjtolton20:04:18

integration.

dpsutton20:04:22

ah ok. cool 🙂

tjtolton20:04:54

I guess if there's no core way to do it, I could always make some kind of function like this:

tbaldridge20:04:24

@tjtolton wouldn't a promise work here?

tjtolton20:04:50

would it? I pretty much narrowed my study of clojure concurency tools to atoms and core.async

tbaldridge20:04:37

look into promise then, you create it via promise and then anything that derefs it blocks until someone calls (deliver p val)

tjtolton20:04:52

oh, dude, that's perfect

tbaldridge20:04:18

You can even do (deref p timeout-ms) to wait awhile before giving up on the deref

tbaldridge20:04:45

can be useful when a bug could cause your tests to hang.

tjtolton20:04:19

yeah, thats exactly what I'm going to do

tjtolton20:04:25

you are the man. thanks to both of you @dpsutton and @tbaldridge

mattly21:04:23

I'm trying to zip together two sequences of unequal length, where I don't know ahead of time which sequence is longer. Is there a simple way to do this? Effectively what I want is for something like (map vector (range 5) (range 10)) to return ([0 0] [1 1] [2 2] [3 3] [4 4] [nil 5] [nil 6] [nil 7] [nil 8] [nil 9])

ghadi21:04:35

are they countable in memory?

mattly21:04:44

theoretically 😛

mattly21:04:55

I'm trying to avoid that

ghadi21:04:28

that will work

spei21:04:28

but it might not interleave in the way that you want

spei21:04:40

i haven't used it in forever

ghadi21:04:13

if you know which one is shorter, you can replace the shorter one with (concat shorter (repeat nil)) then do your original (map vector...)

mattly21:04:59

I'm trying to keep it as lazy as possible though

spei21:04:16

concat and repeat are both lazy

mattly21:04:39

hm.. I was under the impression concat wasn't

mattly21:04:05

well i'll be

wei21:04:52

are there swap! and reset! variants with a spec check?

dpsutton21:04:57

could you concat (repeat nil) to both and then take while the vectors have at least one non-nil value?

dpsutton21:04:11

assuming nil is not a valid member of your original sequence

dpsutton21:04:27

perhaps make your own sentinel of ::no-value etc

mattly21:04:35

@dpsutton yeah that's what I'm going with

noisesmith21:04:02

@mattly the problem with concat is that it’s lazy, leading an intuitive pattern (iterate over some data and concat to a result) to turn into a stack bomb

mattly21:04:24

I was a bit disingenuous with my example, there are also n sequences of uneven length that I'm dealing with

mattly21:04:52

@noisesmith that's kinda the problem with lazy processing in general though, no?

noisesmith21:04:22

well, mixing lazy-processing with eager iteration, definitely (though there are subtler less catastrophic problems too)

dpsutton21:04:26

i just recently was pointed at this

mattly21:04:41

oh yes I remember reading this

dpsutton21:04:51

but i think that's not an issue here

noisesmith21:04:10

right - I meant it as an aside, not a “this is the answer to your problem” kind of thing

mattly21:04:29

it's probably why I had it in my head that concat wasn't lazy

mattly21:04:17

this seems to do what I want: (->> [(range 3) (range 5) (range 6)] (map #(concat % (repeat nil))) (apply map vector) (take-while (comp not-empty (partial filter identity))))

mattly21:04:45

where the first vector there is a substitute for the collection of collections

spei21:04:38

why do you want so many nils? out of curiousity

mattly21:04:07

it's not that I want the nils so much as I want tuples

mattly21:04:16

where the nils are placeholders

dpsutton21:04:22

that would be a cross product, no?

mattly21:04:31

yeah I guess so, now that I think about it

dpsutton21:04:37

but that's not what you're doing

dpsutton21:04:50

you're doing take-while-any

dergutemoritz21:04:04

True, it's not really a join 🙂

dergutemoritz21:04:16

But the outcome is similar

spei22:04:00

i guess you could just do it in a loop recur function

dpsutton22:04:22

well, a cross product would have 3*15*6 elements, and i think here he's gonna have 6 elements

dpsutton22:04:26

so we're pretty far from that

mattly22:04:48

right, no, it's more, swapping columns for rows, if you would

dpsutton22:04:16

really good way to put it

coltonoscopy22:04:56

any game programming clojurists? one of my big motivations for learning and adapting clojure is to write games (transitioning from a background in java, lua, c++, and python-based game programming). curious to hear what your experiences with using clojure in that arena are! i've been tinkering with play-clj thus far

ghadi22:04:04

He gave a great talk at a previous Conj

coltonoscopy22:04:33

i believe i've seen it, he wrote play-clj 😄

coltonoscopy22:04:53

watched another presentation he gave too the other night

darwin22:04:23

@coltonoscopy I used to be a gamedev, hardcore c++ 😉

coltonoscopy22:04:54

@darwin do you miss it? 😉 you still game dev in clojure?

cfleming22:04:48

I’m gradually going to start migrating a lot of the seq usage in Cursive to something eager using transducers for perf reasons. I almost never want anything to be lazy anyway. However I still need a way to provide a flexible iteration strategy over the basic AST forms. After some investigation, it seems like implementing IReduce and IReduceInit is probably the way to go, either in my AST forms or returning reified versions from functions specifying some iteration strategy. Does this seem like a reasonable approach?

darwin22:04:27

@coltonoscopy well, I do, from time to time 🙂 not in clojure, these days I focus on some other things

darwin22:04:00

I don’t think you can generally say that language X is suitable for game dev, you would have to be more specific

darwin22:04:14

clojure is not particularly good, I would guess

darwin22:04:39

(I mean for typical games)

coltonoscopy22:04:31

well, language along with its libraries and paradigms. in this case i guess FRP with clojure in the context of game dev is the end goal

coltonoscopy22:04:57

could be most any FP language AFAIC but i like the elegance of clojure 🙂

darwin22:04:57

I would probably incline to use clojure as a scripting language, and having engine and assets pipeline in C++ or whatever, but depends

coltonoscopy22:04:35

play-clj is a wrapper around libgdx, which thankfully handles the lower-level stuff, which is the best clojure option i've found thus far

coltonoscopy22:04:57

i guess i'm looking to hear about people's experiences in dealing with larger code bases in the context of game dev and what they've struggled with or enjoyed?

coltonoscopy22:04:54

already watched 🙂 unfortunately there was no source and little of it was concrete. also was in haskell

coltonoscopy22:04:01

though was very interesting

darwin22:04:43

He is into Racket these days, at least for scripting VR experiences

darwin22:04:03

kinda Racket as a sane replacement for Javascript 🙂

coltonoscopy22:04:40

oh interesting, i havent actually looked into racket yet, but it looks really cool!

coltonoscopy22:04:44

thanks for the tip! 🙂

cfleming22:04:54

Or as a replacement for C# or lua, which are normally used for game scripting.

coltonoscopy22:04:36

yeah i currently use Lua and LÖVE

coltonoscopy22:04:18

i enjoy coding in that but fancy the idea of going purely functional (or close to it)

cfleming22:04:37

@coltonoscopy Have you seen https://github.com/meric/l2l? @technomancy (of lein fame, among other things) seems to like it.

coltonoscopy22:04:19

looks neat, haven't seen!

coltonoscopy22:04:23

though code like this worries me

coltonoscopy22:04:24

(table.insert output \local \,ref = true)

tbaldridge23:04:22

Yeah, it's not until you use Clojure that you realize that lisp != functional most of the time.

cfleming23:04:35

Lua’s GC isn’t great for pure functional though.

tbaldridge23:04:58

s/Lua's GC/most GCs

cfleming23:04:53

Sure, no doubt.

cfleming23:04:22

BTW @tbaldridge, as a transducery kind of guy, any comment about my question above? https://clojurians.slack.com/archives/C03S1KBA2/p1493331948789484

cfleming23:04:42

Implementing those interfaces seems a bit like black magic, but it looks like the best approach to me.

tbaldridge23:04:51

I'm in favor if it, I've done quite a few crazy things with transducers pretty much all with good results. IReduceInit is a great place to start, and from there you can leverage eduction to treat IReduceInits as seqs

cfleming23:04:29

Interesting, thanks.

tbaldridge23:04:33

and "treat them as seqs" means you can keep the data with the pipeline if you want.

cfleming23:04:19

I think I’m going to try this in a few isolated but perf-critical bits before migrating everything. I haven’t used transducers before so didn’t necessarily want to start by implementing the underlying Java bits 🙂

cfleming23:04:32

But it seems like the best approach.

tbaldridge23:04:34

unfortunately (seq (eduction ... reducible-thing)) ends up calling .iterator on reducible-thing so you are kindof stuck with transduce/reduce but I haven't found that too annoying.

cfleming23:04:43

I think that’s probably ok. And I guess I can (ab)use reducible things to essentially use them as a doall as well, and just ignore the result?

cfleming23:04:33

i.e. when I want to use the same iteration strategy but to perform an imperative action on each element.

cfleming23:04:41

Or is something like doall using eduction better there?