Fork me on GitHub
#beginners
<
2019-11-20
>
parker00:11:12

So if I understand right, having all the source files in src/project-name/ prefixes their ns's (e.g. project-name.foo) and prevents grief if I end up bringing in a lib also named foo? And it sounds like you can nest additional subfolders to organize your stuff, and that will be reflected in the ns's as well? Neat.

bfabry00:11:42

"namespaces are one honking great idea"

bfabry00:11:29

although it's more that the namespace must reflect the folder layout rather than the namespace "will" reflect the folder layout. in your (ns project-name.foo declaration you have to specify the full and correct namespace as dictated by the folder nesting

parker00:11:09

Gotcha, thank you!

vinurs01:11:21

how can i make (+ 0.3 0.6) to 0.9?

vinurs01:11:46

and (+ 0.2 0.1) => 0.30

johnj01:11:39

quick answer is, use bigdecimal

johnj01:11:45

(+ 0.3M 0.6M)

andy.fingerhut01:11:29

Most floating point numbers (Java type float and double, based on IEEE 754 standard) are approximations. This section of the Clojure equality guide article might have some useful info: https://clojure.org/guides/equality#_floating_point_numbers_are_usually_approximations

ScArcher03:11:34

I'm connecting to a Socket with clojure and I'd like to read messages from the socket, then for each message I receive I'd like to call a handler function. I'd like the handler to happen in another thread, but ultimately when it returns a value, I'd like to queue that up to send as a response over the socket. Does anyone have a recommendation on how I should approach this? I've written the code to connect to the socket, send message, and receive messages so far. I'm not sure how to approach the rest.

ScArcher03:11:16

Should I setup separate threads for reading / writing to the socket, so that one can block on reading, the other can just wait for messages, then send them one at a time?

Alex Miller (Clojure team)03:11:11

A common pattern is to have one thread that accepts incoming messages then hands them off to a pool of threads to respond

ScArcher03:11:16

Looking at core.async now. Just unsure of what approach / library that would be appropriate here.

ScArcher03:11:15

In Java, I would probably create a fixed thread pool executor service and could handle message processing that way.

Alex Miller (Clojure team)03:11:25

I’m not sure core.async helps you a lot here, I would just do that

ScArcher03:11:12

Would this be the relevant reading material - https://clojure.org/about/concurrent_programming ?

dpsutton03:11:30

you can look at https://github.com/clojure/clojure/blob/master/src/clj/clojure/core/server.clj#L115-L117 to get an idea of how clojure's built-in socket server works

dpsutton03:11:49

its not quite what you're looking for but perhaps it can serve as a jumping off point

ScArcher03:11:08

Thanks, that's helpful! I'm trying to learn the "clojure" way of doing things and every time I fall back to Java I'm worried I'll implement my solution in a familiar way that prevents me from seeing the benefits of functional programming.

ScArcher03:11:26

I'm basically porting a working c# app to clojure in order to learn Clojure and hopefully provide an example to others I work with on the benefits of Clojure. (The REPL is the best part so far)

dpsutton03:11:55

if you internalize immutable values and write java flavored clojure i don't think you're in a bad spot to create lots of functional things. as you read code and run into tough spots from writing java flavored clojure you'll discover benefits of different ways. but don't let the perfect be the enemy of the good. and always ask questions here 🙂

ScArcher03:11:46

Thanks for the encouragement. So far the "socket" is the only part of the application that isn't "purely functional" so I already see other benefits.

ScArcher03:11:58

(`defmacro ^:private thread
  [^String name daemon & body]
  `(doto (Thread. (fn [] ~@body) ~name)
    (.setDaemon ~daemon)
    (.start)))
So I was looking at the code and I saw (thread ) but I didn't know what that was, so then I saw the definition of a thread macro. Should I just use this as-is and not ask questions 🙂 or do I need to understand a bit about what this is really doing? It looks like it's creating a thread, setting the daemon flag, and executing what's the in body within the thread's run method and starting it up. Is that a valid take? I haven't written or read much on Macros so far.

dpsutton03:11:29

use triple backticks around the region instead of puting them on each line

ScArcher03:11:24

That's what I get for clicking the "code" button 🙂

dpsutton03:11:46

yeah. the real benefit to this is the obvious, it ensures that set-daemon is set. and it also enables a bit of sugar in that it takes a form and wraps that in a function rather than requiring the caller to do so. So this doesn't need to be a macro but makes the usage a bit nicer

👍 4
dpsutton03:11:19

if you see how its used, one of its arguments is the form (accept-connections conn ...). If this wasn't in a thunk then it would be evaluated on the calling thread rather than delayed and invoked on the newly created thread

ScArcher04:11:22

Is there an easy way to make a function synchronized so that only one thread can enter at a time?

ScArcher04:11:48

;; lock protects servers
(defonce ^:private lock (ReentrantLock.))
(defonce ^:private servers {})

(defmacro ^:private with-lock
  [lock-expr & body]
  `(let [lockee# ~(with-meta lock-expr {:tag 'java.util.concurrent.locks.ReentrantLock})]
     (.lock lockee#)
     (try
       ~@body
       (finally
         (.unlock lockee#))))) 
I think I found the answer in the code.

noisesmith18:11:36

this looks like an attempt to do clojure.core/locking from scratch

noisesmith18:11:53

now I see this is in clojure.core.server, I wonder why one would use this instead of locking (or visa-versa...)

Noel Llevares04:11:44

Do I have to know Java before learning Clojure? I want to avoid Java if possible.

Noel Llevares04:11:48

For additional context, I’m comfortable with NodeJS/Typescript, Python, PHP, etc. I did some Java back in the day but didn’t enjoy it.

Rex04:11:34

Clojure uses a lot of underlying Java DS and libs, i reckon you might want to look into common Java DS?

Rex04:11:21

there might be time when you want to make use of Java as well like the code block above with ReentrantLock

seancorfield05:11:00

@noelmartin you can get a long way with Clojure without knowing Java. About the only skill you'll need is reading stack traces IMO

Noel Llevares05:11:50

Nice. I’m backend-oriented so I’d like to start learning Clojure by writing up some backend APIs. Any suggestions for Clojure equivalent of batteries-included web frameworks (e.g. Django, Rails, etc.) and microframeworks (e.g. Flask, Slim, Express, etc.)?

4
ScArcher05:11:19

@noelmartin I'd suggest checking out some of the frameworks referenced in "Web Development with Clojure" - https://pragprog.com/book/dswdcloj3/web-development-with-clojure-third-edition I think Luminus is the framework, but it uses several libraries.

👍 4
Noel Llevares05:11:10

Perfect. I prefer learning from books so this is great!

solf06:11:28

Any way of turning warn-on-reflection only for own source files, and not for dependencies? Or is this a bad idea?

em06:11:57

@noelmartin https://www.youtube.com/watch?v=0EX3UIl-Sd8 This was a great talk that goes in depth and clarifies a fairly standard and idiomatic way of constructing a web application, and is also filled with solid suggestions for further study. With the plethora of choices in Clojure web development, this was really grounding and helped me see not just what choices exist, but why they exist and how each is useful.

Noel Llevares06:11:55

Thanks. I'll watch this.

👍 4
solf10:11:53

Do you append ! for impure functions that read state in either atoms or external databases or should it be only for functions that modify state?

Shuai Lin10:11:58

My 2c: it's a sign for "caution, side effect", so for reading atoms/database I think it's not required.

jaihindhreddy11:11:47

Even clojure.core is not consistent here. - persistent! has bang because it is destructive on the transient argument. - alter and alter-var-root don't need it because they are within the epochal time model. - But swap! despite being in the epochal time model has a !. - run! has a bang because it's intended for side effects - print and related don't even though they're effectful. I can go on but, you get the point. I'll leave you with a snippet if you wanna look at all the stuff in clojure.core with a !:

(->> (ns-publics 'clojure.core)
     keys
     (map str)
     (filter #(re-find #"!" %)))

delaguardo11:11:00

In general side effect is anything that causing unpredicted behaviour. Reading from atom could return different results depending on the state of atom.

solf11:11:12

I'm leaning towards not putting a bang for database reading functions. Being too strict about putting a bang on side effect functions isn't helpful either, as even print has unpredicted behavior

jaihindhreddy12:11:18

The modern usage of the word behaviour is a bit vacuous. One precise definition of "pure function" is "referentially transparent", because a fn that prints something always might return the same output given the same input. But still won't be a pure function as per this definition. Should we name all fns that aren't known to be referentially transparent with a !? Maybe. Maybe not.

henrik14:11:24

There’s a bit of a distinction between “causes side-effect” and “is impure.” You could have a function that returns the same result predictably based on the same input, and causes all kinds of havoc on the side. You could have another function that returns unpredictable results (`rand-int`, for example), but doesn’t mutate anything in the environment. Reading the DB is impure, but presumably doesn’t side-effect. I tend to use bangs for mutations.

mloughlin15:11:29

Scheme has traditionally used ! to denote destructive mutations, and specifically not I/O

Illia Obukhau14:11:17

Hello, clojurians, I need some help from gurus 😉 I have a question about state management in Clojure. As everybody does, to make learning fun I'm trying to build a small game. It's a card game and it has a lot of rules, units, spells, and all this stuff. And the biggest problem that I'm struggling with now is how to organize game state and make it simple to debug & maintain. I spent much time trying to google how people organize the state in web apps and libraries, but it still hard for me. The most confusing part is that I can't find any alternative to "map in a single atom" approach. So, the question is -- how to organize program state (runtime state) in Clojure without tears and is there any way to manage state in isolation

Michael J Dorian14:11:00

A guru I am not, but I'm also writing a game to help me learn and I decided to go for a map in an atom. The map is deeply nested and I have getters and setters that abstract away the really confusing logistics of actually using data that is stored like that. My game, if you wanna see the work of a new clojurian: https://github.com/doby162/witchazzan-server

Illia Obukhau14:11:30

Yeah, I ended up with smth similar, but all this "narrow" functions and "update-in"s are quite disappointing. I just stopped and decided to look for any alternative, but only thing that comes to my mind is FRP based on atoms & watchers

Michael J Dorian14:11:39

For what it's worth, before I went for the nested map I just had global vars that were vectors of atoms, each atom being the state of a single object. The linters hated it because I constantly added and removed things from global lists by re-defing them, but it was way more intuitive and didn't actually cause any problems yet. I think the problem would be lack of thread safety.

🤷 8
Illia Obukhau15:11:42

One of the option is to use Component but I think it too heavy for small game https://github.com/stuartsierra/component

Illia Obukhau15:11:43

I'm more eager to learn atom based approaches

henrik15:11:32

If you’re happy to try out CLJS, you could have a go at it in Reagent with React backing your rendering. It has a more opinionated answer to state with regards to the view = function(data) relationship. You do end up dealing with atoms, however, and the general consensus is that a single global atom is the way to go, with small atoms internal to a component desirable only when there truly is no coordination with the surrounding environment.

Illia Obukhau15:11:51

Well, thank you for the answer. I plan to create main game logic on backend and then start develop client with cljs

delaguardo15:11:26

https://github.com/weavejester/integrant another alternative to component or mount-lite

didibus16:11:28

There's other way

didibus16:11:52

First, is your game multi-threaded or single-threaded?

Michael J Dorian16:11:13

Mine is multi threaded, it's online multiplayer and each player gets a worker thread, plus the main game logic is offloaded to a thread so that the repl can be used to manually inspect game data

didibus16:11:13

If its multi-threaded. You can define your game state in refs. And when you need to modify more than one ref atomically you do it in a transactions.

didibus16:11:06

You can have as many refs as you want

didibus16:11:12

If your game is single threaded, you can use atom instead of refs, they're a bit faster since they don't require transactional machinery for concurrent access

didibus16:11:35

Similarly, you can just have many atoms instead of one atom with a big map in it

didibus16:11:21

For complex games, an entity component system could be used: http://entity-systems.wikidot.com/

didibus17:11:50

But basically that way you can just manage state as you would in any other programming language

Sam Ferrell17:11:10

FWIW I am handling my game state with DataScript and enjoying it a lot. Also using something of an entity-component system. Not sure if its for absolute beginners though.

Sam Ferrell17:11:54

And may not be usable for real-time games... mine is something of a slow-paced MUD

didibus17:11:43

I don't have a lot of experience in game state management in general. But Clojure shouldn't really require you to change the way it's done.

didibus17:11:18

What would have been a Class with data fields is instead a map or a record.

Cameron17:11:37

Yea I was trying to write something up that basically gets to what didibus said; you can break it up the same in Clojure, either by splitting up the top level pieces of your program in your top level function, or keeping them together in one data structure

didibus17:11:40

What would have been a Class with methods are just functions in a namespace

Cameron17:11:47

(although I realized part of the question is what is best)

Michael J Dorian17:11:58

Thanks for the options! I'm only a beginner to clojure so I'm not afraid to try more involved solutions

Cameron17:11:24

and even when you do package it together, you can still write functions that deal with one layer each, rather than writing all the functions to start with the gameobject and 'reach in' and dig around for the things they need and to carefully place their changes in that same spot without disturbing anything, like some spy movie where Frankie Munez is rappelling from the ceiling and carefully replacing the super expensive diamond with some decoy

Cameron17:11:48

don't ask me why Agent Cody Banks of all things was my first example of a spy movie

Cameron17:11:09

Of course, when you write your changes directly just for the things they affect (meaning, you don't write something like

(defn attack-player [gameobject]
  (update-in gameobject [:players :main :health] - 12))
but instead
(defn attack-player [player]
  (update player :health - 12))
Yes, you will still need something responsible for sending those changes to the appropriate spot in your overall data

👍 4
Cameron17:11:37

And usually I suppose I'd do that is in layers like (this is probably gonna take a bit to type up):

didibus17:11:42

Maybe to make things more clear. If you had in an OOP language:

class Player {
  x-location = 0;
  y-location = 0;

  move-by(x, y) {
    this.x-location += x;
    this.y-location += y;
  }
}

class Game {
  player = new Player();

  start() {
    player.move(10, 30);
  }
}
Then in Clojure you can just do:
(ns game)

(def player (atom {:x-location 0 :y-location 0}))

(defn move-player-by
  [player x y]
  {:x-location (+ (:x-location player) x)
   :y-location (+ (:y-location player) y)})

(defn start
  []
  (reset! player (move-player-by @player 10 30)))

Cameron17:11:33

This is sort of hard to come up with on the spot without a real example to pull from but when your top level pieces are all in one map, you might have

(def gamestate {:map { :mobs {:main .. :enemies ..}}})

(defn step-gamestate [gamestate]
  (step-map gamestate))

;; How does the map change in one step of the game?
(defn step-map [map]
  (-> map 
  step-physics
  step-battle)) 

;;I'm not going to destructure here to hopefully be as clear as possible
(defn step-physics [map] 
  (let [ mobs (:mobs map)
         enemies (:enemies map)]
   .. figure out where everyone gets to move to
   .. return new map))

(defn step-battle [map]
  .. get all pairs of battlers,
  .. battle all pairs of battlers 
  .. marge results back into map)

(defn battle [attacker defender]
  .. return attacker and defender after battle)
Where each layer is just responsible for computing changes to its own layer, and each outer layer sends its change functions to its own constituents, which themselves have to send change functions to their constituents.

didibus18:11:16

I'm also not sure you ever need a def ? Won't there be a game loop somewhere?

Cameron18:11:23

and one thing about this is it stays pure for as long as possible (in a real example of course above this there's going to be other things happening, like input being read and passed into step-gamestate), holding off on becoming imperative until its finally necessary, which would happen in the main loop where each step of the gamestate is updating an atom, and where the gamestate is being interpreted to produce things like screen updates and sound effects

didibus18:11:32

Can't the state just carry itself over each recursion

Michael J Dorian18:11:09

why would we want to avoid a def? Putting the gamestate in the global state makes it easy as pie to inspect with the repl

didibus18:11:32

That's fair. I was just trying to point out an atom is never really neccessary. The whole thing can be pure beyond IO

Cameron18:11:33

if you're referring to my def didibus, if you have something like that it would represent the starting point for the gamestate, but in this case I actually wrote it just to show a rough example structure of the gamestate

Cameron18:11:49

and then yes, you'll have an actual atom holding the living state, whether it be in the main function or outside of it

didibus18:11:16

It's fine to have an atom for it if you want to inspect it and all. But if nothing outside the main loop uses it, it doesn't need an atom even. It's just the next iteration of the game state

Cameron18:11:49

ah that's what you were saying

Cameron18:11:24

In fact, I wasn't even thinking about it, but as I do I can say none of my recent projects have any atoms (currently at least), just main functions that recur

👍 4
didibus18:11:39

Ya, I think what confuses most people is that they don't get that you don't need any state. Your functions don't manipulate global state anymore. Your examples are good in that regard. The reason your layers don't need to access the global state map and reach in and out is because you don't need mutation anymore. You take data in and return the modified version of it out. It starts from the top layer down, and once everything returns your game state is rebuilt and you now have the new game state. Put that in a recursive game loop and voila :p

didibus18:11:20

At some point, you need side-effect where you take user input from IO and return the sound and graphics and all back to IO. But the actual state is just old-state to new-state based on user actions, and the current game state events.

didibus18:11:17

Easier said then done. But the way @UBCSS6NGK showed with their code examples is a good starting point for sure.

Cameron19:11:44

Aha if you're telling me specifically why my examples don't need access to a global state map, I'm well aware, although I'm sure you're speaking in general, or a little of both! I used that example exactly to show that, not accidentally show it. I want to be clear about the atom thing; my surprise is not knowing that you don't need an atom, it was that I was not even thinking about that at all, or the fact that I even realized that I haven't used an atom myself in a while. But overall, the addition or removal of the atom is not the important part to me (another reason I wasn't thinking about it), the important part is expressing your turtles changes pure all the way down, and not giving that responsibility to the atom (sending the atom the step-game function does this, but if you start breaking down that function and you directly feed it to the atom, now you are making the atom responsible for composing changes with mutation that could have been pure, and now you've not made it pure all the way down). After that point, keeping the atom is not adding or removing anything, in the same way Theon was still around when he lost his name; your program does have state, in the sense that the final layer of your program is always finally imperative, finally brought to life, and at that point the atom just serves to give a name to it, one extra window for observation (and is a win there). You indeed don't define your changes in term of that state though, they just describe what should happen, without being tied to an implementation. With the example where even the top level function on gamestate is just recursive, you might say don't have to even ever explicitly mention the state elephant in the room. Eventually, a pure function must decay into an effectful imperative one to bring forth life, and so something will compose it with an imperative implementation -- whether its explicitly done or behind the scenes. Of course, there are certain higher level aspects I would like to think on (does keeping an atom mean the next person is more incentivized to add changes imperatively?) , so note I am not actually speaking for potentially complex higher level trade offs like that (a psychological trade off, for instance). This is really hard to word, so some of the things may not be exactly what I'm trying to express. @U0K064KQV

Cameron19:11:50

oh god that's longer than I thought

didibus20:11:51

@UBCSS6NGK Sorry, I think things got confusing down the road. I agree with everything you said, and I think the examples you gave are good ones.

Cameron20:11:21

aha thank you!

didibus20:11:37

I was just pointing out in theory, the game loop could also just carry the top level state locally. That would be the FP way. It's okay in Clojure to put it in an atom if that makes debugging or other things easier.

ScArcher16:11:40

I'm currently trying to read a line from an inputstream and call a function to handle that line. I need this in a loop until the line is nil. What's the best way to do this? It's like I need a let binding in a while condition or something.

dpsutton16:11:52

(doseq [line lines] (f line)) should work for you. you can also use loop if you want a bit more control over when to recur

ScArcher17:11:58

So where would my readline go? in place of "lines" ?

Lu17:11:21

(with-open [rdr ( file)]
             (reduce
              (fn [parsed line] your logic)
              ""
              (line-seq rdr)))

Lu17:11:57

if the line is nil call reduced on it to break the loop

ScArcher17:11:53

@U11BV7MTK I tried (doseq) but it treated the first returned string as a sequence of characters. Any idea what I did wrong?

ScArcher17:11:45

(doseq [line (.readLine reader)] (println line))

dpsutton17:11:17

that's on a single line. so it returns a string and that's a sequence of characters

👆 4
dpsutton17:11:24

check our line-seq

dpsutton17:11:31

(source line-seq)

dpsutton17:11:08

the source of which is probably a just a hair snazzier than the solution you are writing or were just about to write

ScArcher17:11:31

got it - (doseq [line (line-seq r)] (println line))

dpsutton17:11:57

right. whatever generates the collection of lines

bringe18:11:43

I have a question that I feel like there is a simple answer to, but it's not clicking. I have a string arg in a function that I want to convert to edn with clojure.edn/read-string. Sometimes the string arg will be a string representation of edn, and sometimes it will just be an actual string, like "hello world". If it's just a normal string, I want to leave it as is, but if it's a string representing edn, I want to call clojure.edn/read-string - because if I call that on a normal string like "hello world" I just get hello returned (a symbol I believe, because it was expecting edn).

dpsutton18:11:47

do you control the source of the string?

bringe18:11:48

So how can I make this work for both strings and edn-as-strings? Do I need to check if the string is edn-as-a-string? How would I do that?

bfabry18:11:02

"hello" is a valid edn string. so it's gunna be a bit hard

dpsutton18:11:12

this mimics when people try to handle a single element and a collection of elements in a single function

bringe18:11:32

No, it's coming from timbre, and I'm adding my own output-fn

dpsutton18:11:49

you could check if its some type of coll

dpsutton18:11:57

if symbol just use the string rep

bringe18:11:45

Oh I think that would work... thanks

bfabry18:11:43

probably not important to you but fwiw that's narrowing your api so that it only accepts a subset of edn. if you have external consumers or whatever you might want to document that

dpsutton18:11:04

although "[DEBUG] no user-id" might give you trouble 🙂

shaun-mahood19:11:54

@brandon.ringe One other thing to watch out for (clojure.edn/read-string "80") will work fine, but (clojure.edn/read-string "08") will throw a NumberFormatException. It caused me all sorts of trouble with one of my early Clojure projects - I was reading in months and it worked totally fine for January - July, but once I hit August the whole thing stopped working properly 🙂

bringe19:11:51

Interesting! Thanks for the heads up.

noisesmith19:11:44

it's the same thing clojure does if you enter 08 at the repl

noisesmith19:11:02

user=> 010
8

bfabry19:11:14

ya, numbers prefixed with 0 are octal and 0x are hex according to java, and so for clojure

noisesmith19:11:46

user=> 36rbatman
683306015

😮 12
noisesmith19:11:01

user=> (Long/toString 683306015 36)
"batman"

Lennart Buit20:11:33

clojure must be the first language that has a literal for base 7

noisesmith20:11:29

010 is base 8 - or do you mean eg. 7r10 ?

Lennart Buit21:11:39

yeah, I find it funny that you can have a strange base like 7 like that

noisesmith19:11:10

or more relevant to the initial point:

user=> 10r08
8

sysarcher21:11:01

Is there a better way to do this?

dpsutton21:11:34

(zipmap (map (comp keyword str) (range)) apps)

dpsutton21:11:49

but keywords on integers aren't great in my opinion

👍 4
sysarcher21:11:19

ooo... I see what you did there... thanks!!

sysarcher21:11:38

you see the type of map I'm looking to generate, right? any suggestions?

dpsutton21:11:11

didn't i make exactly that?

sysarcher21:11:02

no, I was referring to the comment you made about keywords on integers not being great

seancorfield21:11:14

It's poor practice.

seancorfield21:11:11

Even tho' Clojure "allows" stuff like :0 it should be avoided -- either use actual numbers as the keys in the map or use keywords based on symbol names.

seancorfield21:11:31

That has maps with :id and other keys.

sysarcher21:11:49

yeah, I got distracted by this problem... 🙂

seancorfield21:11:28

(map #(hash-map :id %1 :name %2) (range) apps)

👍 4
seancorfield21:11:48

Or mapv if you want a vector of maps rather than a sequence.

sysarcher21:11:48

I found mapv earlier.. 🙂 Thanks for these hints !!

sysarcher21:11:19

ooo... I just got rid of my ugly imperative style for loop I'd written earlier today!! thanks a lot guys

noisesmith23:11:43

also, {0 "yello" 1 "yello2" 2 "yello3"} is allowed

noisesmith23:11:14

you don't get the convenience that the key implicitly calls get, but you can work around that by calling the map, or just using get

👍 4