Fork me on GitHub
#clojure
<
2018-08-16
>
ilikeOPHeroines01:08:53

Hey guys i am a student trying to learn FP programming and Clojure. I want to implement a game so i was wondering what library/framework is best to learn? I figure using libraries from the JVM would be difficult to use functionally if i don't have much experience.

pauld01:08:34

you might want to check out https://github.com/oakes/play-cljs he has a clojure version as well. (play-clj - but he's focusing more on play-cljs).

soulflyer01:08:34

@danial.javady clojure tends to use more of a mix and match approach than other languages, ie there is no one library/framework you should learn, but there are hundreds of useful tools to do specific tasks. Generally there is no problem in adding as many of those as you need. Hit us up with specifics and we can suggest things. You may be better off with clojurescript for game programming though, and lein-figwheel setup comes with a link to a flappy bird demo that might be of interest to you: https://github.com/bhauman/lein-figwheel.

ilikeOPHeroines02:08:59

thanks alot guys! I was a bit hesitant on diving right in because I know just the basics about front-end dev. Would it help to be proficient in JS, HTML, and CSS? I really hate JS... Right now i want to get an idea of how to develop functional applications properly.

the2bears02:08:40

@danial.javady you can check out #clojure-gamedev as well, though it's pretty quiet. I've had fun using play-clj for what it's worth.

ilikeOPHeroines02:08:27

thank you, play-clj seems like a nice way to avoid the browser ^^

soulflyer02:08:40

Thats a no on the js. It wouldn't hurt to know it, but you don't need it, and in the early stages of learning functional programming you are better of sticking to languages that don't let you cheat and use mutable things...

soulflyer02:08:03

Should qualify that, "don't make it easy for you to use mutable things". Clojure(script) does allow it for the situations where it is really necessary.

pbaille08:08:26

Hello, i’ve just hit this:

(let [f (constantly "foo")]
  (defrecord A []
    Object (toString [x] (f x)))) 
;=> CompilerException java.lang.RuntimeException: Unable to resolve symbol: f
i know i can do a def instead of the let block but it puzzles me a little. any thoughts?

noisesmith13:08:30

defrecord doesn't use closed over values - it's an intentional design decision to prevent usage of the "functional closure as object" idiom from scheme / common lisp

noisesmith13:08:08

we have actual objects, using a closure as if it's an object is almost always the wrong thing in clojure

alice08:08:56

I have a question, it has little to do with clojure other than that's how I'll be implementing it, but how do I update a single line in a (potentially but not always) gigantic file efficiently

alice08:08:12

It'd be possible to collect all the lines before and after, apply some change to the line I need, and put all of them back together again to write to a file

alice08:08:32

but the file size is potentially greater than the amount of ram the program will have access to

alice08:08:50

I need some intelligent/lazy way to update single lines in gigantic files tldr

schmee08:08:23

sounds like you need a database troll

alice08:08:45

Not the answer I'm looking for >.<

schmee08:08:16

forgive the trolling, but if you find yourself needing to change files like that it might be that you’re not using the right tool

schmee08:08:29

I’ve been stuck in the same situation and I haven’t found a good answer

alice08:08:52

Well I mean, databases update their gigantic files in-place somehow

alice08:08:23

But they're pretty well designed I guess...

schmee08:08:59

yes, a DB storage engine is pretty elaborate stuff

jgh08:08:21

this is the same reason mp4 headers are at the back of the file 😉

schmee08:08:31

can you stick the file into a DB somehow? or is that out of the question?

sundarj08:08:06

if the line you need to change happens to be near or at the end of the file it'll need to fit in memory anyway right

alice08:08:44

So here's the deal, I'm trying to work on a personal project where I'm creating a super-super-super primitive file-based edn database (I know this is bad I'd never do it in practice) because I want to play around with creating a query language within clojure

jgh08:08:47

or break the file up into many smaller files

alice08:08:00

I could always just split them up, the database is not the focus here

alice08:08:29

I just want to be able to explore the creation of a query language without working in terms given to me by some other database

schmee08:08:56

so you’re basically writing your own storage engine, cool! 😄

alice08:08:22

That's the idea, but I really am not focusing on the storage part, frankly I'm not smart/educated enough to design that well

schmee08:08:43

they key, as you just mentioned, is to split stuff up in some way so that there are many small files instead of one huge

alice09:08:23

I'll probably just make a disgusting filesystem db that's like root/db-name/table-name/record0...record9999

alice09:08:36

I guess that's the easiest solution

alice09:08:53

feels so gross though >.<

schmee09:08:24

doing this in a smart and efficient way entails reading a lot of books about the subject 😛

jgh09:08:40

there's probably some really clever way to partition files and keep them balanced but i dont know enough about this stuff to even think of where to start

alice09:08:08

I tried to look into implementing a b-tree and doing things at least halfway in the right direction

lmergen09:08:22

almost certainly going to involve repartitioning and/or a global index file

alice09:08:24

but it's way too much for me to understand or even put the time into since storage isn't really the goal here

schmee09:08:21

then the “disgusting”, easy solution seems like the way to go! 😄

alice09:08:17

Well thanks then, sorry for the stupid question

schmee09:08:58

not stupid at all 🙂

simple_smile 4
wekempf12:08:52

Databases work by using fixed size records, mostly. For variable length stuff they break it up into fixed size blocks. If you're dealing with a "line" in a text file, you have no real way to do this. Best option is to write the changes to a temp file and then move the temp file to replace the original.

alex-dixon13:08:08

How can I write this in Clojure?

final ComparatorOptions comparatorOptions = new ComparatorOptions();

        final Comparator myComparator = new Comparator(comparatorOptions) {
            public String name() {
                return "my comparator";
            }

            public int compare(final Slice s1, final Slice s2) {
                return s1.data().length - s2.data().length;
            }
        };
I’ve tried using proxy…getting an error CompilerException java.lang.UnsupportedOperationException: name,
(defn comparator [options]
  (proxy [Comparator] [options]
    (name [] "foo")
    (compare [^Slice a ^Slice b] -1)))

Mark Addleman13:08:13

In our project, we don’t write comparators directly. Instead, we write regular Clojure two-arity predicates (ie, functions that take two parameters and return truthy or falsey) and then use the clojure.core/comparator to generate the Java comparator. https://clojuredocs.org/clojure.core/comparator

alex-dixon13:08:36

Was there a reason you went with that instead of the native comparator?

Mark Addleman13:08:57

Sorry, I read your post too quickly. I didn’t realize you were talking about a RocksDB-specific thing

Mark Addleman13:08:04

If you’re talking about using clojure.core/comparator instead of using proxy to build a comparator, you end up with same thing and clojure.core/comparator is a lot easier 🙂

alex-dixon13:08:34

Np. Good suggestion…looking like I may have to do battle with this abstract class though 😞

Mark Addleman13:08:35

Oh, my bad. I read your first message too quickly

Mark Addleman13:08:46

I didn’t realize you were talking about a different comparator 🙂

alice13:08:38

If anyone cares, I did it!

(interpret-operation [:insert test-db :people {:name "alice" :age 18}])
(interpret-operation [:insert test-db :people {:name "pants" :age 40}])
(interpret-operation [:query test-db {:table :people :select :all}])
==> ({:name "alice", :age 18} {:name "pants", :age 40})
(interpret-operation [:query test-db {:table :people
                                      :conditions {:age #(> % 20)}
                                      :select :one}])
==> {:name "pants", :age 40}

👍 24
alice13:08:42

That was a fun exercise

Mark Addleman13:08:07

@alice if you’re interested in a Clojure/EDN query language, check out https://github.com/halgari/odin

alice13:08:34

This is a lot more complicated than mine lol

alice13:08:38

Interesting

Mark Addleman13:08:09

Yeah, it’s pretty cool. I’ve used it for some toy projects and I’ve hoping someone would fork it and keep running with it

Mark Addleman13:08:48

In my experience, the kind of thing you did gets the job done without all the extra complicated stuff 🙂

alice13:08:41

I don't forsee myself needing anything like this in the future tbh, there's a lot of stuff that does this kinda thing better that's already been proven effective in the industry

alice13:08:47

but definitely a fun exercise

alex-dixon14:08:20

Looks like this patch would make my above question easier: https://dev.clojure.org/jira/browse/CLJ-1255. Is the only alternative AOT and gen-class?

bronsa14:08:43

for now yes

bronsa14:08:52

vote on the ticket tho ;)

👍 4
alex-dixon14:08:51

I’ve read another option that avoids AOT is writing the implementation in Java then calling from Clojure

marlenefdez14:08:46

Hi all! I'm fairly new to Clojure, I was generally curious if there's a preferred Clojure style in terms of defining things recursively on your own, vs using loop and recur. So, the code below implements the same function twice -- count the number of bits that are equal to one in the binary representation of that number. Is there one answer that's preferred? Or is a personal preference at this point? Thanks! `

marlenefdez14:08:05

(defn count-bits
  "Write a function that takes an (unsigned) integer as input, and returns
  the number of bits that are equal to one in the binary representation of
  that number."
  [n]
  (cond
    (= n 0) 0
    (= (bit-and n 1) 0) (count-bits (bit-shift-right n 1))
    :else (inc (count-bits (bit-shift-right n 1)))))
vs
;;Similar solution with use of loop - seen in slns:
;; (defn count-bits-loop
;;   [n]
;;   (loop [i n
;;          c 0]
;;     (if (zero? i) c
;;         (recur (bit-shift-right n 1) (+ c (bit-and i 1))))))

restenb14:08:05

loop-recur is the idiomatic way here, so that one is preferred

danm14:08:28

@marlenefdez Clojure doesn't detect implicit tail-recursion. IIRC it's an issue with the way the JVM works, it's just not possible on it

danm14:08:56

So if you define it yourself, you are running the risk of blowing the stack if the recursion goes too deep

ghadi14:08:41

loop-recur is considered pretty low-level (but performant). If you can do it with sequence operations, do that.

danm14:08:50

With loop and recur, you are explicitly telling Clojure that you are doing tail recusion, and it will moan and refuse to compile if it's not actually tail recursion, and will make sure to not blow the stack

danm14:08:30

But often I see people coming from other languages using loop a lot more than they should/could, because you can often use map and reduce and such

danm14:08:08

In fact, I think I've only ever written a single loop in several years of Clojure

ghadi14:08:33

not your question, but you can call the java stdlib for that: (Long/bitCount n)

marlenefdez14:08:32

@ghadi yes, I noticed that, but thought that would be too much "cheating" if the whole purpose of that tiny function was practice 🙂

marlenefdez14:08:10

@carr0t thank you! I'll try to stick with loop and recur

ghadi14:08:48

@marlenefdez you need unsigned-bit-shift-right

ghadi14:08:29

(defn bit-count [n]
  (->> n
    (iterate #(unsigned-bit-shift-right % 1))  ;; sequence of shifts
    (map #(bit-and % 1))   ;; select only last bit
    (take 64)
    (reduce + 0))) ;; sum them up
@marlenefdez that's a sequence-oriented approach

👍 4
marlenefdez15:08:31

:thinking_face: thank you, @ghadi I wouldn't have thought to solve it that way, I need to learn more about "sequence oriented approach"

ghadi15:08:03

loop-recur is faster for sure, but also they are harder to reason about

ghadi15:08:03

it's a mindshift to think in terms of the collections functions

marlenefdez15:08:31

it definitely is, thank you!

marlenefdez15:08:59

i'm also generally new to clojure, so it's a mind shift in general 🙂

jsa-aerial15:08:35

Also, note that in your case, the recur is in tail position so you could just do:

jsa-aerial15:08:40

Which is pretty idiomatic and also should be as fast as the loop / recur

denik15:08:15

Looking for a combination of partition-by and filter or a take-while that doesn’t terminate but instead drops values and returns a new collection with the next sequence of vals that satisfy pred, example:

(let [pred #(or (>= 5 % 1) (>= 10 % 8))
      nums (range 11)]
   (into [] (partition-by pred) nums)
   ;; => [[0] [1 2 3 4 5] [6 7] [8 9 10]]
   ;; desired => [[1 2 3 4 5] [8 9 10]]
   )

denik15:08:51

that will combine the values, no? looking for partitioning

noisesmith16:08:38

how would that predicate give you those groups for that input?

noisesmith16:08:37

throw away everything where it returned false, and collect the ones that were consecutive?

denik16:08:36

sure, that’s the algorithm. what’s the straightforward way to do this in clojure?

noisesmith16:08:10

my first instinct is (fn group-consecutive [pred? coll] (when (seq coll) (lazy-seq (cons (take-while (pred? coll)) (group-consecutive (drop-while (complement pred?) coll))) - it's not quite right (might end up with nils in the output you don't want) but is probably close to something that works

nathanmarz16:08:46

@denik with specter that's:

(select (continuous-subseqs #(or (>= 5 % 1) (>= 10 % 8))) (range 11))
;; => [[1 2 3 4 5] [8 9 10]]

💯 8
denik16:08:49

specter to the rescue!

jgh19:08:02

is there a decent pattern for creating graphs of functions? For example I want to start at an input, have a few transforms along the way, and end at an output. Each function passes a context object to the next function. The graph can split or merge too, so each stage could have multiple outputs or inputs.

mauricio.szabo19:08:58

Multiple inputs for a single function? Lots of "if apply this transformation only if these conditions match"?

lwhorton19:08:23

sounds like the chain of responsibility pattern / interceptor pattern to me. check out pedestal or re_frame

john19:08:40

reminded me of interceptors too. Though I don't think you can fork and join chains in the same interceptor chain.

jgh19:08:13

yeah specifically im thinking of approximating pipes and filters in a functional way instead of object-oriented. I'll take a look at interceptor or cor

john19:08:05

what would be the condition that decides whether an output goes to one or more different functions? Some list of predicates provided with the graph to whatever the run-fn is?

john19:08:29

Or some conditions stored on the functions in the graph?

jgh19:08:51

the graph would be defined by json or something like that, or even hardcoded, so it's like in -> fn1 -> [fn2, fn3] => fn4 -> out. so maybe the functions just take an input and return the result that is of the same time but transformed, and then the next function takes it from there

jgh19:08:12

but there would be some metadata that different functions respond to...like maybe one responds to unicode characters, the other responds to ascii, etc.

john19:08:16

I'd probably just use maps, for exploratory purposes

jgh19:08:37

how do you mean?

john19:08:50

But I'm not sure whether you'd want to store those branches [fn2, fn3] directly in the graph or as an external parameter you pass into whatever traversal function

jgh19:08:21

yeah, i'm not sure. The way i've done it before was with C++ and it's really painful tbh

jgh19:08:45

i mean it's a great pattern for keeping things separate but it's painful to setup

john19:08:58

(:fn1 "data" (:fn2 1 2 (:fn3 3)) 4)

jgh19:08:26

that would get quite deep haha

john19:08:32

That would just pull fn1 off the map

john19:08:00

The fns could all be top level. You just provide it a domain specific AST and fns get dereferenced from some op-map as necessary. Then your branching and joining is dictated by some idiom in your AST.

john19:08:54

Not sure how you'd cleanly design that idiom though.

john20:08:40

ask and you shall receive 😂

hiredman20:08:16

specialized for lists, but I think in principle it would work for any monad

john20:08:23

That's pretty cool

jgh20:08:40

huh really cool

jgh20:08:43

thanks guys

jonahbenton20:08:44

Onyx does this at scale. I wrote a simple library around a similar concept: https://github.com/jonahbenton/flower The idea of data driven control flow I think is very powerful.

jgh21:08:02

ok thanks for the link, i'll take a look at your implementation

john21:08:06

Yeah, essentially, if instead of def'ing all your fns in a namespace, you def them as vals on a map, then you can pretty much treat it like an immutable map-fn-ns that you can pass around. There's a lot of interesting ways you can take that.

mfikes21:08:20

This discussion reminds me of Prismatic Graph, from what little I remember about it. http://plumatic.github.io/prismatics-graph-at-strange-loop

john21:08:27

Yeah, flower looks pretty sweet. I like that "fns return either maps or booleans" idiom. Because maps are functions of their keys, you can probably construct any given logic with just map applications

john21:08:31

and because clojure maps share structure underneath, you can create virtual execution graphs that could never fit in memory.

jgh21:08:37

@U04VDQDDY yes this is also a good resource.

jonahbenton21:08:40

Yeah, graph is this idea on steroids.

jgh21:08:15

thanks for the discussion guys, this has been really helpful! A lot to think about and try now...