Fork me on GitHub
#beginners
<
2018-01-06
>
dadair00:01:31

Hmm interesting thanks

ackerleytng02:01:14

what's good practice for def ing constants in clojure?

ackerleytng02:01:54

is it better to use (let []) in the function using the constants so the constants are grouped with the code, or something more like globals

ackerleytng02:01:20

what if the constants are used across different clj files?

noisesmith04:01:38

@ackerleytng ideally clojure has nothing but constants, they don't need to be specially treated

noisesmith04:01:13

if the constant is something that is independent of any input to the function, I'd put it in a def

noisesmith04:01:57

the special case is values that should change

noisesmith04:01:59

for those, don't use def (or at least not def directly) - either use function arguments so that the value can be immutable and has no top level binding, or use a safe mutable container (atom, ref, agent) inside a def for the rare cases where you need a mutable singleton

ackerleytng04:01:10

sure, thanks!

ackerleytng07:01:06

I'm trying to add clojurescript to an existing server side clojure application and i got

ackerleytng07:01:11

Failed to compile "resources/public/js/main.js" in 1.744 seconds.
----  Could not Analyze  resources/public/js/out/cljs/pprint.cljs  ----

  Invalid :refer, var cljs.core/IWriter does not exist

----  Analysis Error : Please see resources/public/js/out/cljs/pprint.cljs  ----
Figwheel: initial compile failed - outputting temporary helper application to resources/public/js/main.js

ackerleytng07:01:21

where should i start looking?

ackerleytng08:01:11

got it! my directory structure was wrong.

Aron11:01:46

if I have some input data that I need to translate two times before i can use it, wrapping the two transformation functions into a single one does it make my code simpler or more complicated?

hansw12:01:06

i think i would keep those two steps seperately and just compose the 2 functions when you need to apply them in succession

Aron12:01:58

thanks, i should've pointed out that this is a theoretical question, i am trying to understand this

ackerleytng12:01:45

in clojurescript, why does this not work?

app:cljs.user=> (js/google.maps.LatLng. 1.365035 103.644231)
#object[Object (1.365035, 103.64423099999999)]
app:cljs.user=> (apply js/google.maps.LatLng. [1.365035 103.644231])
nil
app:cljs.user=>

bronsa13:01:24

interoppy stuff (constructors, methods) are not first class

bronsa13:01:28

so you can't use them higher order

bronsa13:01:59

you have to wrap them in a function

ackerleytng13:01:37

how do i do that?

ackerleytng13:01:19

(fn [x y] (js/google.maps.LatLng. x y))?

ackerleytng13:01:18

ok it works, thanks!

debamitro16:01:44

How can I explore a Clojure or ClojureScript codebase? Is there something similar to cscope/gtags/global etc. from the C/C++ world?

dpar16:01:30

what should I use to schedule a function for execution at a certain time?

noisesmith16:01:47

jvm clojure? there's a library called at-at, or you can directly use ScheduledThreadPoolExecutor it's not a super difficult api and clojure functions are Callable

akiroz17:01:34

There's also juxt/tick, the API is based on lazy seq of times

dpar17:01:41

@akiroz why is schedule in a deprecated namespace though?

dpar17:01:05

@akiroz it seems the main api for scheduling function in the tick library is deprecated

akiroz17:01:25

IntelliJ Cursive?

noisesmith17:01:11

you can programmatically explore in a repl as well, we have clojure.repl/dir, clojure.repl/doc, clojure.repl/source (all of these are in scope by default when you start up)

noisesmith17:01:38

there's also all-ns and ns-publics which when combined allows programmatic exploration

akiroz17:01:59

@teikfaiv looks like they're moving to a new API recently :thinking_face:

noisesmith17:01:54

those errors make me think you have versions of things out of sync

Chris O’Donnell17:01:01

@akiroz @teikfaiv I believe tick is migrating their timestamp object from ZonedDateTime to Instant

zsck17:01:45

Whoops, I didn' mean to highlight you there.

akiroz17:01:46

thanks for the update, It's been a while since I used tick

zsck17:01:34

Should I be using openjdk-8 for Clojure 1.8 and 1.9?

noisesmith17:01:08

it's probably the best combination of up-to-date and performant yes

akiroz17:01:12

I'm still on 8, are there issues with 9 + clojure?

noisesmith17:01:21

there were at one point at least

noisesmith17:01:40

most of the problems are not with clojure itself, but with plugins and tooling (leiningen and boot related stuff)

akiroz17:01:57

seemed like the world was gonna explode when java 9 modules came out

zsck17:01:10

I'll try downgrading.

zsck17:01:22

That did it!

justinlee18:01:10

what’s the point of making an empty defprotocol and then implementing it? I see this in the reagent source code where it does a (defprotocol IReactiveAtom) and then implements it with a (deftype RAtom ...

noisesmith18:01:45

in order to test that something implements the type

justinlee18:01:22

but if the protocol is empty, what information does that convey?

noisesmith18:01:52

whether something implements it or not

justinlee18:01:26

i guess my question is more, why would you want to do that?

justinlee18:01:38

what would be lost if it wasn’t there?

noisesmith18:01:41

you would need some other way to mark certain types, but exclude others, that could be extended from outside your code

noisesmith18:01:59

so you can't use a simple conditional, or nobody else could make a thing for which that check returns true

noisesmith18:01:51

it's one of the things people do when inheriting, but isolated from the other properties of inheretence

justinlee18:01:43

I’ll have to think on that. When looking at this example, it seems like the IReactiveAtom protocol is really a collection of several other protocols, because the properties I’d expect in a reagent atom are all of the protocols that the (deftype RAtom…) implements. I guess I would have expected the defprotocol to collect all of those things together and relabel them as the meaning of the IReactiveAtom. https://github.com/reagent-project/reagent/blob/v0.8.0-alpha2/src/reagent/ratom.cljs#L117

noisesmith19:01:11

you can't do protocols on protocols though

noisesmith19:01:53

if you look at how clojure.core does things, protocols and interfaces contain as little as possible, and complex sets of behaviors are composed by implementing multiple protocols / interfaces

noisesmith19:01:39

so it makes sense to me that one of those protocols is the one that says "you can react on this thing" and it doesn't need to define other behaviors

dpar19:01:41

I run boot run and it seems to be download the entire http://clojars.org and http://maven.org archives. It never ends. Is that normal?

dpar19:01:45

clojure1.5 clojure.16 clojure1.7 1.8alpha1 alpha2 beta4.pom ...

noisesmith19:01:28

if they are poms, those are just dependency info

noisesmith19:01:36

and they are very small

noisesmith19:01:57

I would expect a very large number of poms to get downloaded, then only the jars you need based on analyzing all the poms

dpar19:01:36

alright it stopped, I hope it's just for the first time

noisesmith19:01:54

right, it's a cache

justinlee19:01:54

@noisesmith thanks for the help. i don’t get it yet, but i think i just need some more time with the language

noisesmith19:01:47

@lee.justin.m have you ever used a language with inheretence?

justinlee19:01:21

yes sure. i think the problem is that i’m trying to shoehorn this into a C++/Java paradigm and it just doesn’t work that way

noisesmith19:01:14

I think one key here is that clojure avoids concrete inheretance - you will rarely see anything that inherits something other than an interface or protocol

dpsutton19:01:19

it could be a marker for assumptions. it looks like there are only two types that implement IReactiveAtom. so if something satisfies that, they know what assumptions come along with that. ie, what other protocols are implemented. also, it's clear that it's an internal data type rather than something from outside of reagent's codebase

justinlee19:01:30

it really reminded me of opaque typing in a ML type language or flowtype, but I just don’t know what good that is in a dynamic setting

noisesmith19:01:42

so where in java or c++ you would say "pass in something that inherits Foo", in clojure you would say "pass something that implements IFoo"

noisesmith19:01:06

@justinlee yes it's a direct translation of that

noisesmith19:01:13

I think at least...

justinlee19:01:23

right but in those languages you’d never have an empty interface, as far as i can recall

justinlee19:01:25

in ML you can make the language perform matching and inference based on a type tag, and in flowtype the purpose is for a library author to say “do not look inside here this thing will change”

justinlee19:01:06

but both of those are static analysis concepts

noisesmith19:01:10

yes - instead of pattern matching we use protocols or defmethod, both of which can look at what protocols you implement for dispatch

dpar19:01:58

@sekao are you able to run and try your javafx app in the repl?

noisesmith19:01:12

so if you had, for example, (defmulti render-thing type) and then (defmethod render-thing IReactiveAtom ...) - you can make sure your custom object hits that implementation of the multi by implementing that protocol

justinlee19:01:33

@noisesmith yea okay that does make sense. thanks

justinlee19:01:52

i hadn’t thought about multimethod dispatch

noisesmith19:01:02

and I bet reagent has something much like that hidden down in there

justinlee19:01:31

probably! i feel like a complete idiot reading this code 🙂

noisesmith19:01:45

@justinlee pure speculation - I bet reagent uses the presense of IReactiveAtom to decide whether a change should trigger rerenders

justinlee19:01:29

actually the whole reason why i’m reading the code is because i’m trying to figure out exactly what reagent is doing when it triggers updates

noisesmith19:01:35

and then IReactiveAtom can't define deref - that's already defined by another protocol...

noisesmith19:01:43

or the protocol that allows watching, etc.

noisesmith19:01:41

@lee.justin.m also I'd say that understanding any of this brings us well out of the scope of #beginners

justinlee19:01:05

@dpsutton see that’s exactly what confused me: if the user ran (ratom? thing) and got true you’d expect it to do all the ratom things (like be derefable and swappable and so forth). but you could, if I understand this right, implement IReactiveAtom with an empty object, since it requires nothing. that would be very confusing. (maybe the point is that IReactiveAtom is an internal concept and it is just a convenient way for the library author to tag something? i was just expecting the language itself to enforce some kind of duck typing here: i.e. an IReactiveAtom does all of this stuff.)

justinlee19:01:36

@noisesmith but I’m a beginner 🙂

noisesmith19:01:07

going depth first I see

justinlee19:01:20

its a character flaw, honestly

dpsutton19:01:43

it's a tag from the reagent lib that this thing will act like a reagent atom

noisesmith19:01:59

yes, that's what I suspected

dpsutton19:01:07

if you make your own type and implement this it's understood that you should know what you are doing. and what you are doing is implementing the other protocols, which are the actual api surface of a ratom. so that a ratom can be used by normal clojure functions while doing ratom things

justinlee19:01:00

okay i get it now

sundarj19:01:39

i suppose an alternative would be to make ratom? smarter, checking all the individual protocols

noisesmith19:01:54

regarding duck typing, we don't have that, and we don't have protocols that extend other protocols - those facts should also help

noisesmith19:01:42

@sundarj but since that protocol exists only to inform reagent that you are like an ratom, the other checks are redundant, because clojure never enforces that you extend any of a protocol's methods

sundarj19:01:16

yeah, right

noisesmith19:01:16

so checking the extra protocols provides no info, as it's no guarantee that the methods are implemented, so you can skip all that and just check for the marker protocol

sundarj19:01:43

i meant instead of the dummy protocol

noisesmith19:01:58

oh - but then I can't use atoms any more inside my reagent code

noisesmith19:01:09

I see that though, OK

noisesmith19:01:32

(that is, non reactive atoms)

sundarj19:01:34

good point

dpsutton19:01:00

@lee.justin.m it's not enough to check that they satisfy all of the other protocols. as (atom {}) would pass on all of the others

dpsutton19:01:12

that protocol is a tag that this object has assumptions baked into it

dpsutton19:01:23

> "Like clojure.core/atom, except that it keeps track of derefs."

justinlee20:01:29

yes there is this interesting line where its being used almost as documentation but it also has technical implications. i think it took me some time to understand where the line is