Fork me on GitHub
#beginners
<
2018-03-25
>
Michael Fiano00:03:32

Should predicate functions ending with ? only return true or false, or does it make sense to return the input unaltered or false...can this still be considered a predicate function with ??

Michael Fiano00:03:41

I'm guessing not. This seems to be like some vs not-any?

Michael Fiano00:03:48

Correct me if I'm wrong 🙂

mfikes00:03:40

All core predicates have returned true or false, except a couple of recent ones in spec; this causes lots of commotion 🙂

mfikes00:03:33

With "predicate" being a function ending in ?

mfikes00:03:45

If you are curious: 91 votes for this ticket https://dev.clojure.org/jira/browse/CLJ-2141

Michael Fiano00:03:59

I see. That makes sense. I suppose this function I'm writing is doing validation, and so should pass input through as a noop or return false, so probably isn't a "predicate"

eem6701:03:11

I just got a new laptop (it uses Windows 10) and I'm trying to download leiningen and gorilla repl. I used to have a mac and I remember it being really easy to get these downloaded on that. I keep getting an error when I download leiningen that says "Exception calling "DownloadFile" with "2" argument(s): "The request was aborted: Could not create SSL/TLS secure channel." How can I get this to work?

ryrobes02:03:02

@marwe22t Off the top of my head - make sure that you are using an elevated cmd prompt instead of PowerShell. Then you can set the "cert ignore" shell evn vars that the lein.bat script suggests - provided you have curl or wget installed (you can use choco to install https://chocolatey.org/install).

Michael Fiano11:03:06

Is a function's parameter vector accessible within its body?

rakyi11:03:10

not directly, I think, but you can probably use destructuring for what you are trying to achieve https://clojure.org/guides/destructuring#_sequential_destructuring

ackerleytng13:03:20

I was using (-> url a b c d), but url could be nil, and functions a b c and d could have returned nil too. If any of them return nil, i would like to break the chain and return different error messages. What's the most idiomatic way to do this? Exceptions?

john13:03:42

and check for nil in each test clause

john13:03:26

oh, yeah, some-> is even more directly what you're asking for

joelsanchez13:03:07

however...if you want to return different error messages, cond-> or exceptions may be more useful...`some->` is better for shortcutting and returning nil

john13:03:25

@mfiano if you define your function thusly (defn afn [& params] ... then params will essentially be a sequence of the contents of your argument vector

mfikes14:03:57

some-> and fnil are probably friends 🙂

Michael Fiano16:03:56

Similar question to the one I had yesterday: Is it good practice to make ? predicate functions return false as the falsey value instead of nil? Often this involves another branch via if and therefor more instructions.

mfikes16:03:41

I don't know if the perf consequences in Clojure are important or not. Oftentimes in ClojureScript, if it is absolutely critical, the return value of a predicate is additionally annotated with (ClojureScript-specific) ^boolean meta to avoid a checked if. More on that meta is here http://blog.fikesfarm.com/posts/2015-12-04-boolean-type-hints-in-clojurescript.html

bronsa17:03:20

way less important in clojure since there's no way to return primitive booleans from functions

Michael Fiano17:03:16

Interesting. Yeah I have no prior experience with JVM or JS so I don't know how it is at generating code. My experience just says that branching in a tight loop is bad

noisesmith17:03:36

@mfiano: you can make a boolean without if using the boolean function btw, and if your concern is performance, the price of mapping nil /false to boolean and everything else to true seems very low

noisesmith17:03:04

it would be overshadowed by things like hash lookups and even those are cheap

Michael Fiano17:03:41

This is what I have, but it will be called in a tight loop. How to make it better?

(defn room-overlaps?
  [room1 room2]
  (if (or (> (:x1 room1) (:x2 room2))
          (> (:x1 room2) (:x2 room1))
          (< (:y1 room1) (:y2 room2))
          (< (:y1 room2) (:y2 room1)))
    false
    true))

noisesmith17:03:54

also consider that in normal clojure code every function you call is being looked up on each invocation (because we allow redefinitions) and that's definitely more expensive than boolean

noisesmith17:03:41

@mfiano: that code can't ever return nil

noisesmith17:03:54

or returns false for fall-through, and > is boolean

Michael Fiano17:03:55

I know. false or true

bronsa17:03:04

drop the if and wrap the or in a not

noisesmith17:03:21

what I'm saying is that if can be replaced by not

Michael Fiano17:03:01

I see. I didn't know that. I'm much too used to Common Lisp with nil standing in for false.

bronsa17:03:45

note that

user=> (not nil)
true

bronsa17:03:27

but, in clojure.core there re some predicates that return nil instead of false already -- edit: not anymore

Alex Miller (Clojure team)19:03:21

are there? I can’t think of an example, curious if you had something in mind

bronsa19:03:58

uh, I was thinking of qualifyed-symbol? & friends but forgot about CLJ-2141

mfikes17:03:12

I'm curious, in ClojureScript, all of the stuff in room-overlaps? disappears and you end up with JavaScript involving native >, <, and ||. I suspect Clojue ends up generating similarly optimal bytecode in that case.

bronsa17:03:43

not in clojure

mfikes17:03:22

You end up with calls to a runtime or or somesuch?

bronsa17:03:03

no, no calls to or

mfikes17:03:08

By the way @mfiano if you need to know, you can look at the bytecode

bronsa17:03:09

we just expand the or to nested ifs

Michael Fiano17:03:46

@mfikes Interesting, but considering I don't even know Java, I'm not sure how much that will help me 🙂

mfikes17:03:06

I bet you would grok it, coming from a native background. It is just another kind of assembly

mfikes17:03:29

(Not really Java at that level.)

bronsa17:03:52

jvm bytecode is super easy to understand if you have some familiarity with stack languages

mfikes17:03:20

Also, without going to that level, Criterium is awesome for perf questions

Michael Fiano17:03:29

My only experience prior to this week is Common Lisp

mfikes17:03:08

Are you writing a game or something where perf is critical?

mfikes17:03:04

There are good Java profilers that can help see the big picture of where your code is spending its time.

mfikes17:03:20

(They work at the bytecode level and work fine with Clojure.)

Michael Fiano17:03:47

Right now I'm just noting possible areas for later optimization as I learn the language.

mfikes17:03:22

One easy way to save hassle is to set *warn-on-reflection* at the top of each namespace. Reflection can be ridiculously expensive.

Michael Fiano17:03:51

Hmm I don't even know what they are.

mfikes17:03:25

Namespaces?

Michael Fiano17:03:44

No, reflection.

Michael Fiano17:03:00

Anyway, I just figured out how I can change the above code to not use if or even not

mfikes17:03:17

Here is the Criterium tool, that is really useful for ensuring code has good perf, without having to break out a full-fledged profiler: https://github.com/hugoduncan/criterium

mfikes17:03:15

It makes it rather easy to see if a speculative change to a function will make it run faster for a given input.

Michael Fiano17:03:40

(defn room-overlaps?
  [room1 room2]
  (and
    (> (:x2 room1) (:x1 room2))
    (< (:x1 room1) (:x2 room2))
    (> (:y2 room1) (:y1 room2))
    (< (:y1 room1) (:y2 room2))))
This is equivalent if I'm not mistaken.

Michael Fiano17:03:45

Oh great, thanks

mfikes17:03:12

I wonder if you can put a lot of that into one big <

Michael Fiano17:03:29

I don't think so, because there are 8 scalars

Michael Fiano17:03:56

I mean, the x and y of each rectangle are independent in calculating intersection

jonrob17:03:14

hi all, i'm trying to build my clojurescript project - works in figwheel but when i build it and load the output js in browser, nothing happens

jonrob17:03:26

i'm sure i've read somethign about this in the quickstart before, but i can't find it now

jonrob17:03:34

any ideas where to start debugging?

mfikes17:03:51

When you build it are you adding :advanced optimizations to the mix?

mfikes17:03:40

(I ask because that introduces the potential to break things, relative to the :none which is used at the REPL.)

jonrob17:03:06

yes, looks like the figwheel template adds those

jonrob17:03:09

i'll try disabling

mfikes17:03:36

Check your JavaScript console to see if it is saying anything.

jonrob17:03:59

yeah - annoyingly it's not saying anything, makes me think nothign is being executed

jonrob17:03:37

ah got some errors with optimizations disabled though - so that's helpful 🙂

jonrob17:03:45

so with optimizations off, i'm getting 404 for goog/base.js and goog/deps.js

Michael Fiano18:03:14

Why does clojure do biased rounding?

Michael Fiano18:03:29

Rounding on most languages such as Common Lisp or Python, rounds half towards even to prevent biasing. On Clojure (or maybe Java) however, the rounding is biased and always rounds half up: Common Lisp, Python, etc 1.5 = 2 2.5 = 2 3.5 = 4 4.5 = 4 Clojure 1.5 = 2 2.5 = 3 3.5 = 4 4.5 = 5

Michael Fiano18:03:02

is there a function to do unbiased rounding?

bronsa18:03:36

java.lang.Math/round

Michael Fiano18:03:02

That's what I tested with. It's biased

bronsa18:03:04

user=> (Math/round 1.8)
2
user=> (Math/round 1.2)
1

Michael Fiano18:03:27

Unbiased rounding is important for statistics and accurate mathematics

Michael Fiano18:03:44

Otherwise the average of X samples will be artificially shifted upwards

Michael Fiano18:03:00

This is why most languages do not just round half upwards

mfikes18:03:35

@mfiano

user=> (binding [*math-context* MathContext/DECIMAL64] 
  (Math/round 1.5))
2
user=> (binding [*math-context* MathContext/DECIMAL64] 
  (Math/round 2.5))
3

mfikes18:03:29

That's supposed to be half-even... perhaps it is not *math-context* which controls it...

user=> MathContext/DECIMAL64
#object[java.math.MathContext 0x7216fb24 "precision=16 roundingMode=HALF_EVEN"]

Michael Fiano18:03:05

Yeah that's not half-even

mfikes18:03:13

Oh, you need to use BigDecimal..

Michael Fiano18:03:55

(map #(Math/rint %) (range 0.5 5)) ; => (0.0 2.0 2.0 4.0 4.0)

Michael Fiano23:03:00

What is the proper way to iterate until an arbitrary condition is met, collecting the results of the body function call into a list/vector?

dpsutton23:03:35

Reduce and reduced would work. Loop as well

Michael Fiano23:03:57

I tried loop, but i don't know what i'm doing wrong. i always get a list of 1 item

dpsutton23:03:04

Not sure if you're a loop common lisper or not

dpsutton23:03:26

Got a gist I can look at?

Michael Fiano23:03:39

The last function is where I'm trying to collect into a list

dpsutton23:03:26

that appears to always return stage

Michael Fiano23:03:58

What I'm ultimately trying to do is return a new stage hashmap which has all of the rooms stored under a new key

dpsutton23:03:52

and the call to (make-room stage) isn't captured anywhere. so you'll just recur

bronsa23:03:07

it looks to me like you're trying to use clojure as if it had mutability

bronsa23:03:35

in particular that place-room function is a very bad code smell for that

Michael Fiano23:03:45

Honestly, I don't know enough clojure yet to know what I should be doing

mfikes23:03:47

For example doseq should probably be for, and other things to return stuff

mfikes23:03:16

At the bottom is the notion that you can't change a Room instance

dpsutton23:03:32

going from the name of the function, add rooms, presumably you have a list of rooms somewhere and you want to add something?

Michael Fiano23:03:16

add-rooms is generating a bunch of rooms (rectangles) of varying sizes, and placing them randomly on a stage (2d grid), skipping ones that overlap

dpsutton23:03:21

ah. so your make-room function makes a random room and returns nil or the room if it doesn't overlap

dpsutton23:03:34

and you need a way to keep making rooms until you get a good one?

Michael Fiano23:03:36

I want to store the list of successful rooms in the stage hashmap

mfikes23:03:31

Perhaps one place to start is to include stage as one of the bindings in the loop / recur? (So you can keep adding to it.)

mfikes23:03:48

(In addition to tries.)

dpsutton23:03:49

so i would make create-room not take the stage. you can always ask for a new room. and then you can loop or recur and add it optionally as you go

dpsutton23:03:32

and just a small nitpick, but shadowing core functions is a really subtle pain point. you call something max. if you refactor later that can really be a pain

Michael Fiano23:03:59

Yeah that was on my list of things to change.

mfikes23:03:28

The key idea to fix this up is to make the functions return values that you use. (They can't mutate the values passed to them.)

Michael Fiano23:03:31

How should make-room not take stage? i'm not sure how i would check overlap then

dpsutton23:03:49

you're putting two things into one function. give me a room and check if it overlaps

Michael Fiano23:03:19

Right but a room size depends on properties of the stage hashmap

dpsutton23:03:01

(loop [stage whatever
       tries 0]
  (if (< tries max-tries)
    (let [new-room (make-room)]
      (recur (if (overlaps stage new-room)
               stage
               (add-room-to-stage stage room))
             (inc tries)))
    stage))

mfikes23:03:28

The key idea ^ is that an if is used, and either the unmodified stage is returned by the if, or a modified new stage

Michael Fiano23:03:12

I see. Thanks. Let me play around with that idea for a few

mfikes23:03:20

add-room-to-stage would return a modified stage

Michael Fiano23:03:13

Hmm so what would add-room-to-stage look like?

dpsutton23:03:57

it seems like its just an update. your stage is just a map with a rooms key. so (update stage :rooms conj new-room) or whatever data structure that :rooms contains

Michael Fiano23:03:35

Ok I'm not familiar with that function yet. I got some reading to do. Thanks

dpsutton23:03:07

i'm not too familiar with games but is it risky to put a non-deterministic room building algo in there?

dpsutton23:03:24

there's a chance that this thing just loops and never satisfies what you need, right?

Michael Fiano23:03:33

Everything is non-deterministic about this. It's a procedural random world builder. Everything depends on the seed of the RNG

dpsutton23:03:11

right. but i meant the function takes a stage and looks at the existing rooms and bulids a room that will fit, rather than looping and making a random one and hoping it fits

Michael Fiano23:03:18

Yes, it will loop until either the estimated rooms count is met, or an upper bound to stop at

Michael Fiano23:03:54

I'm following a particular algorithm I already ported to 2 other languages.

dpsutton23:03:45

ah. that's why it looked setq-y 🙂

Michael Fiano23:03:02

If you're curious, it will look like this when finished: https://www.michaelfiano.com/projects/gamebox-dgen/

dpsutton23:03:39

super cool!

Michael Fiano23:03:38

It is probably a bad thing to do as my first Clojure application, as it's very undertministic and everything depends on the mutability of the RNG seed

Michael Fiano23:03:45

I'm having fun though