Fork me on GitHub
#beginners
<
2017-10-28
>
Lucas Barbosa15:10:34

Is there any place that I can find more about using Java IO channels in Clojure? Every time I search something related to Channel or Async or Non-blocking, I get something related to core.async and that’s not what I want ;(

Lucas Barbosa15:10:05

I want to do async non-blocking IO in Clojure using the Java methods for that (unless there’s a better way built into Clojure itself)

noisesmith15:10:18

@lvbarbosa why would any of the info be clojure specific?

noisesmith15:10:35

other than interop, which is the same for any class you use

noisesmith15:10:16

my typical approach is to use google to figure out how to do the thing in java, then translate

Lucas Barbosa15:10:31

I am specifically thinking about this method: https://docs.oracle.com/javase/9/docs/api/java/nio/channels/AsynchronousSocketChannel.html#read-java.nio.ByteBuffer-long-java.util.concurrent.TimeUnit-A-java.nio.channels.CompletionHandler- I am not sure how to pass a callback function there. Do I need to implement that interface in a custom type?

noisesmith15:10:30

you can use reify or proxy or defrecord or deftype to make something implementing that interface

noisesmith15:10:36

if it's one off, probably reify

noisesmith16:10:50

(reify CompletionHandler (failed [this exc att] (log/error "completion handler failed" (pr-str exc) att)) (completed [this result att] ...))

ashnur20:10:55

how can i get a random english word in clojure?

eggsyntax20:10:25

@ashnur (rand-nth (clojure.string/split (slurp "words.txt") #"\n")) (assuming words.txt in root dir of project)

eggsyntax20:10:48

I get "soft-soaper".

ashnur20:10:31

yeah, but i don't have words.txt. i tried [helpshift/faker "0.2.0"]

ashnur20:10:07

thanks. but now i will never know why i can't install libraries

eggsyntax20:10:56

Or you can slurp it from the web.

(rand-nth (clojure.string/split (slurp "") #"\n"))

eggsyntax20:10:11

I get "fakers".

ashnur20:10:16

that's very cool

ashnur20:10:33

i am trying to find a module that provides this feature

noisesmith20:10:08

the docs for helpshift/faker seem straightforward https://github.com/helpshift/faker

eggsyntax20:10:14

Plus my randomly chosen word appears to be directing you to that particular lib.

ashnur20:10:20

i am trying again with newer clojure version, last weekend it was not working

ashnur20:10:55

yeah, i can't require it, it just says No such namespace: helpshift/faker.generate, could not locate helpshift_SLASH_faker/generate.cljs, helpshift_SLASH_faker/generate.cljc, or JavaScript source providing "faker.generate"

sundarj20:10:32

it's a clojure library, not a cljs one

noisesmith20:10:03

oh, if you are using cljs that's different yeah

ashnur20:10:39

i thought that the two are only different in very tiny details

ashnur20:10:49

like bigints and threads

sundarj20:10:45

language-wise perhaps, but java and javascript are entirely different environments

ashnur20:10:19

but if the language is the same, it can be compiled to multiple environments?

ashnur20:10:37

i am sorry, when i was learning javascript in 2012, substack wrote a tool called browserify that reads the nodejs specific stuff and replaces with with browser modules that provide the same api, so i was expecting something similar to be in place here too

sundarj20:10:28

you cannot use Express in the browser

sundarj20:10:44

just like javascript, some projects work on both server and client, others do not

ashnur20:10:15

hmm, maybe not the best example. i got your point

ashnur20:10:30

and thanks for the info about cljc!

sundarj20:10:05

any time 🙂

ashnur20:10:47

this is the actual way to do express in the browser

ashnur20:10:15

gotta love browserify 😄

sundarj20:10:53

haha, i consider myself corrected 😉

sundarj20:10:29

although in any case, it's not actually express running in the browser, just an express-like thing 😛

sundarj20:10:35

oh nevermind, i'm wrong there too

Michael Stokley20:10:41

i've got a map with strings as keys and vectors of strings as values. i'm using the elements of the vectors as keys again for the map. would it make sense to use keywords as keys and vectors of keywords as values instead?

noisesmith20:10:09

I don't see why that would be useful

Michael Stokley20:10:47

fair enough. guess i'm not 100% when to use the keyword data type - i often see it used as map keys

noisesmith20:10:28

it makes sense when the value is a literal, or if you have a single key that you want to use as a function and look up in various maps

noisesmith20:10:19

if what you have is a map from strings to vectors of strings, I don't see how converting them all to keywords would help - it seems like it would just introduce a bunch of needless conversions

Michael Stokley20:10:44

conversions between?...

noisesmith20:10:57

strings to keywords then back to strings

noisesmith20:10:08

or is it arbitrary whether they are keywords or strings?

Michael Stokley20:10:11

i think so far it is arbitrary. i can't think of any reasons why i need them as strings. but i can't think of any reason why i'd want them as keywords, either.

Michael Stokley20:10:19

so maybe i'm just making a mountain out of a molehill

noisesmith20:10:49

+user=> (take 100 (iterate (comp rand-nth {"foo" ["bar"] "bar" ["baz" "quux"] "baz" ["foo" "bar"] "quux" ["foo"]}) "foo"))
("foo" "bar" "quux" "foo" "bar" "quux" "foo" "bar" "quux" "foo" "bar" "baz" "bar" "baz" "bar" "baz" "bar" "quux" "foo" "bar" "quux" "foo" "bar" "quux" "foo" "bar" "baz" "bar" "quux" "foo" "bar" "quux" "foo" "bar" "quux" "foo" "bar" "quux" "foo" "bar" "baz" "bar" "quux" "foo" "bar" "quux" "foo" "bar" "baz" "bar" "baz" "foo" "bar" "baz" "bar" "quux" "foo" "bar" "baz" "bar" "baz" "foo" "bar" "baz" "bar" "quux" "foo" "bar" "quux" "foo" "bar" "baz" "foo" "bar" "baz" "foo" "bar" "quux" "foo" "bar" "baz" "bar" "quux" "foo" "bar" "baz" "bar" "quux" "foo" "bar" "quux" "foo" "bar" "baz" "bar" "quux" "foo" "bar" "quux" "foo")

Michael Stokley20:10:56

that's what my map looks like now... the elements of the vectors are again themselves keys. i want to represent a graph but have all the nodes as top level keys.

noisesmith20:10:14

that's called an adjacency-list

Michael Stokley20:10:24

what does the (take 100 (iterate... example illustrate?

noisesmith20:10:36

I'd decide what the type of the identifiers should be based on the use case - all else being equal why not numbers?

Michael Stokley20:10:13

hmm, yeah. why not.

noisesmith20:10:38

it illustrates that you can do arbitrary navigation elegantly with clojure built ins

noisesmith20:10:54

without the (take 100 ...) wrapper, it would be infinite

noisesmith20:10:01

so printing it would never finish

Michael Stokley20:10:04

ah - because it's 'lazy'?

noisesmith20:10:14

because it's lazy and also has no stop condition

noisesmith20:10:41

this is a common situation with iterate - it needs a consumer that knows when to stop

Michael Stokley20:10:53

hey, can i ask another question? in python we append an underscore to avoid collision/(shadowing?). is that an idiom in clojure or do namespaces make it unnecessary?

noisesmith21:10:53

typically we avoid shadowing, but no, we don't tend to use underscores

noisesmith21:10:36

if you have two things that are related, you'll sometimes see pairs like foo* followed by foo, or foo followed by foo' then foo'' etc.

Michael Stokley21:10:04

but if i just want a local variable that happens to share a name with a clojure built-in, i should think of a synonym?

noisesmith21:10:33

since scope is very explicit, you can shadow as long as it's in a small scope and you know you don't need the original

noisesmith21:10:09

it's good to avoid naming data with the same name as a commonly used function (eg. name or count) because that can lead to subtle bugs that are hard for readers to see

Michael Stokley21:10:57

i see. thank you!

athomasoriginal21:10:54

It is my understanding that in clojure we do not technically assign a value to a variable we are actually binding a value to a variable. Would this statement be correct? Piggybacking off the above, I notice that the word variable and symbol are used interchangeably. I believe in this case it would be correct, but would it be more accurate to say that there is a hierarchy that would look like this:

symbols 
    |
    |
variables
As in, all variables are symbols, but not all symbols are variables

noisesmith21:10:30

symbols are mapped to vars in namespaces

noisesmith21:10:42

when unquoted, they are resolved when used

noisesmith21:10:14

they are also mapped to bindings in local contexts (let, fn, loop, etc.) which doesn't involve mutable vars

noisesmith21:10:45

a var in a namespace is not a binding, it's a mutable container holding a value inside a mutable hash

sundarj21:10:04

by default, a symbol is resolved into a var, which is resolved into whatever that var holds:

map
#object[clojure.core$map 0x7d7658b5 "[email protected]"]
but you can also refer to the symbol itself:
'map
map

(type 'map)
clojure.lang.Symbol

noisesmith21:10:46

it's used to access a local binding, if applicable, after that it's used to look up a var

noisesmith21:10:54

locals take precedence, and are not vars

noisesmith21:10:40

@tkjone also, just to be clear, symbols are never automatically resolved, but there are specific contexts where resolution happens

noisesmith21:10:00

for example ('+ 1 2) returns 2, because the symbol is not resolved

athomasoriginal21:10:13

Okay, so using this as an example:

(def name "Greg")
We would say that * 'name is a symbol * name is a variable (or a resolved symbol) But is the example above not a binding?

noisesmith21:10:02

hmm - it is a namespaced global binding actually, right

athomasoriginal21:10:19

Because, and this could be the wrong way to put it, there is no way to re-assign name. I would have to call (def name "James") to change it

noisesmith21:10:31

that reassigns name!

noisesmith21:10:41

it mutates the var holding "James"

noisesmith21:10:09

(which shadows clojure.core/name which is a function)

athomasoriginal21:10:17

I guess, I am wondering, and this is the weeds, what clojure is doing under the hood - does it redeclare the whole thing?

noisesmith21:10:31

it mutates a var

noisesmith21:10:48

literally there's a mutable container of type clojure.lang.Var, the namespace owns a mutable hash-map from symbols to vars

athomasoriginal21:10:50

okay, so the container name is not reassigned a new spot in memory, I am just replacing it's value

noisesmith21:10:48

unless you destroy and recreate the ns first, or remove the var from the ns first (clojure.tools.namespace does things like this)

athomasoriginal21:10:35

hmmm, perhaps some of my confusion also comes from the fact that when I do this:

(def name "Greg")
We would say that the root binding of name is "Greg" - correct? But at the same time, the above is assigning and not binding? Sorry guys, just want to make this clear for myself 😞. And to give some background, I notice that some article like to draw a distinction between assigning as one would do in JS but in Clojure we are binding.

noisesmith21:10:53

root bindings only matter for dynamic vars

noisesmith21:10:33

and cljs doesn't use vars does it? - it's a whole other thing going on there

athomasoriginal21:10:39

Sorry, ClojureScript does not use vars? This is article I am referring to: http://clojurebridge-berlin.org/community-docs/docs/clojure/def/ in which they call the above var binding

noisesmith21:10:10

sorry, it does use vars now, my info was out of date

athomasoriginal21:10:00

So a root binding only matters for dynamic vars but non-dynamic var do have a root binding correct? https://clojure.org/reference/vars

noisesmith21:10:11

yes - in practice you don't usually see root bindings mentioned unless you are dealing with dynamic vars, but both dynamic and static vars have root bindings

noisesmith21:10:06

so when a value is looked up, you either find it in a local scope (not reified as an object, not mutable) or it's found via the namespace (mutable, reified via ns and var objects)

athomasoriginal21:10:27

Nice, so that makes sense now and with that in mind, why is there a distinction drawn between assigning and binding in clojure? I am not sure what the difference is 😞

noisesmith21:10:51

an assignment can be changed later

noisesmith21:10:18

(via different args to a function or recur)

noisesmith21:10:00

but it can't be mutated mid usage - it's just a value

noisesmith21:10:23

in a repl, if you are changing code and don't want to restart, you need to be able to redefine things at namespace scope

noisesmith21:10:05

for local scopes (let blocks, functions, etc.) there's no simple repl driven workflow that would need to do that, and there's a lot of things that are simpler by lacking that feature

athomasoriginal21:10:58

Okay, I think I get it now. assignment could be understood as this:

temp = 0

function incTemp () {
   temp = 5
}
We are re-assigning in this case, but unless you were using a dynamic-var and the binding form you would not be able to do the above - hence assignment operators

noisesmith21:10:22

eh, you can use intern anywhere

noisesmith21:10:27

it's a bad idea, but you can do it

noisesmith22:10:09

or def anywhere to replace the value, if you know the var name at compile time (intern is more flexible) - but we understand as a community that things like this are typically signs that you are doing it wrong

noisesmith22:10:25

but that only works for namespace level stuff - for locals there's no object to mutate - there's values and their value already propagated to the usage sites which are not accessible without doing not portable hacks

athomasoriginal22:10:28

So is the idea of assigning vs. binding more of an ephemeral thing? By this I mean, there are ways to mutate the vars values, but in Clojure its just a frowned upon practice.

noisesmith22:10:15

yeah - if that's the distinction you are making - but I've also seen fn args or let bindings use both terms assigning / binding so that terminology is a bit imprecise

athomasoriginal22:10:06

Exactly, yes, I just see a distinction being made, but I never see any examples of assignment vs binding that would suggest it is a structurally different thing

noisesmith22:10:06

(unless I've been misunderstanding that stuff, which is also possible)

antique22:10:58

Is there an idiomatic way to handle errors in Clojure? It seems like most of the suggestions involve exceptions. I just ran across the cats library as well which looks very neat.

noisesmith22:10:11

there are many alternatives, none are common, and no matter what you use you will also have to deal with exceptions

athomasoriginal23:10:28

Looking over the relationship between a symbol and a var, I am wondering if this is correct: * symbols are mapped to vars in the namespace So if I do something like this:

(def my-name "Jay')
and then I ran (ns-map 'current-ns) I would get all the mappings including the one I did above which would look like this:
name #'user/my-name
Which means that my-name is the symbol and it is mapped to the var user/name. Yes?

eggsyntax23:10:24

@tkjone yeah, that matches my understanding (although grain of salt; it's not something that comes up much for me).

athomasoriginal23:10:08

Thanks all, his has been a huge help!

eggsyntax23:10:10

You've probably already seen it, but https://clojure.org/reference/vars gets into some of this stuff.