Fork me on GitHub
#off-topic
<
2018-12-18
>
eccentric J00:12:24

Any metalheads here in the channel?

24
todo00:12:49

as opposed to a periodic skew to round numbers?

andy.fingerhut00:12:06

I believe many implementations of 'round' round down <odd_integer>+0.5, but round up <even_integer>+0.5 (or vice versa), in an attempt to make the round error more 'balanced'.

andy.fingerhut00:12:14

Very likely some folks who do lots of calculations with floating point numbers have done some kind of numerical analysis kind of thinking to determine that this is in some ways better than always rounding down, or always up.

todo00:12:12

I've yet to hear of a good argument for this, except: people calling 'round' function are sloppy, so we will make assumptions about their data, and then show that round-to-even is good.

todo00:12:46

It seems that for the person doing the analysis, you should never call a function unless you know exactly what it's supposed to do.

andy.fingerhut00:12:53

Probably way more than most people care to know about this topic here: https://en.wikipedia.org/wiki/Rounding

andy.fingerhut00:12:25

I agree that people should not pick one of the alternatives mindlessly.

andy.fingerhut00:12:38

That doesn't mean everyone makes the choice mindlessly, though.

👍 4
todo01:12:00

I'm arguing that people should explicitly define the type of rounding they want.

andy.fingerhut01:12:56

In some programming environments, I'd bet you $1 that round() is explicitly specified, and doesn't always round half up, or always round half down. Otherwise that Wikipedia page wouldn't have so many alternatives on it.

todo01:12:08

I don't want to bet real $ (or crypto); I do think that people should explicitly use floor/ceil instead of calling round; and one can get round-to-nearest behaviour via simply floor(x + 0.5);

andy.fingerhut01:12:48

If someone determined that it was better to round to nearest odd integer in their application, you would deny them this choice?

todo01:12:12

No, I would prefer they define a round_to_nearest_odd function instead of just calling "round" 🙂

todo01:12:41

I'm saying the rounding behaviour of your program should be explicit and not dependent on standard lib impl of round.

todo01:12:42

Let's call truce on this, somehow I think we're arguing over something that increases neither of our productivity. 🙂

andy.fingerhut01:12:46

That looks pretty explicit, even though it doesn't require saying what mode you want on every call -- you may choose to do it once for your whole application

andy.fingerhut01:12:14

I was in truce the whole time 🙂

dpsutton01:12:25

Round to even is a common and known goal and is what many people explicitly want

dpsutton01:12:53

I don't know what you mean by she skew to round numbers. But taking the floor or ceiling of every measurement would certainly put pressure in only one direction on your measurements

todo01:12:11

Your argument boils down to: here is a distribution where round_tlo_even is better than floor. Therefore, round should do round_to_even I am saying: we should have explicit functions floor, ceil, round_to_nearest_int, round_to_nearest_even, and not have a round function at all.

dpsutton01:12:37

Not sure of any language that agrees with you. Most rounds are documented which way they go and often configurable with a strategy. The default is often round middle to even. But most languages also have floor and ceiling so your fears are assuaged as well

todo01:12:36

Well, if python did what I suggested, it wouldn't have the problem demonstreated above on 2.7 vs 3.0 rounding that started all this.

dpsutton01:12:22

I agree they should have done rounding correctly to begin with. But I like that they fixed their mistake :)

dpsutton01:12:03

I'm just partial to round to middle because of my chemistry classes back in high school. Preventing systemic bias is important in the physical science

kulminaator09:12:28

I dont mind that they use one or another, but if you change the maths of your language then may just as well change the name :)

Lennart Buit10:12:48

they explicitly said that Py3 wasn’t backwards compatible with Py2 tho

Lennart Buit10:12:06

I think that given that they wanted to break compatibilty, Python actually found one of the more sane ways to do so

👍 4
eccentric J21:12:25

Story time! So a few days ago, a friend shared this Python snippet with me…

eccentric J21:12:33

Impressed, I thought “What would that look like in Clojure?” After a few questions in #beginners I ended up with

(ns map-comp.core
  (:require [clojure.pprint]
            [clojure.string :refer [split]]))

;; Functions to replicate python's os.walk functionality

(def directory? (memfn ^java.io.File isDirectory))
(def list-files (memfn ^java.io.File listFiles))

(defn separate-dirs-from-files
  [files-list]
  (reduce (fn [[dirs files] file]
            (let [name (.getName file)]
              (if (directory? file)
                [(conj dirs name) files]
                [dirs (conj files name)])))
          []
          files-list))

(defn build-file-tree
  [^java.io.File file]
  (cons file
        (separate-dirs-from-files (list-files file))))

(defn file-tree-seq
  [root-dir]
  (->> root-dir
       ()
       (file-seq)
       (filter directory?)
       (map build-file-tree)))

;; Replicates the functionality of the python snippet

(defn relative-name
  [path dir]
  (let [dir-path (str dir)]
    (string/replace dir-path (re-pattern path) "")))

(defn directory-with-no-files?
  [[file dirs files]]
  (empty? files))

(defn pair-file-with-dirs
  [root-path [file dirs]]
  [(relative-name root-path (.getPath file))
   (vec dirs)])

(defn dir->map
  [root-path]
  (->> (file-tree-seq root-path)
       (drop 1)
       (filter directory-with-no-files?)
       (map #(pair-file-with-dirs root-path %))
       (into {})
       (clojure.pprint/pprint)))

(comment
 (dir->map "/Users/jay/Projects/map-comp/test/fixtures"))

eccentric J21:12:15

Realizing that is just not the same I realized I could write a library that behaves like Python’s os.walk, then another library to add map comprehension as a macro:

(ns example.core
  (:refer-clojure :exclude [replace])
  (:require [file-tree.core :refer [walk-seq]]
            [for-map.core :refer [for-map]]
            [clojure.string :refer [replace]]
            [clojure.pprint :refer [pprint]]))

(defn relative-name
  [root path]
  (replace path (re-pattern root) ""))

(def root-dir "/Users/jay/Downloads/test/resources")

(for-map [[path dirs filenames] (walk-seq root-dir)
          :let [parent (relative-name root-dir path)]
          :when (and parent (empty? filenames))]
 {parent dirs}))
Now a Clojure, has map comprehension too 😄

eccentric J21:12:57

In retrospect it’s kind of a shit story. Needs a better villain… which I’m pretty sure is me. But it is my first published macro!

😂 4
andy.fingerhut21:12:18

Any story involving significant learning is a good one in my book.

👍 12
eccentric J21:12:51

I did learn a lot. It REALLY reinforced that Clojure code is just data and that with tools like macros, we can evolve the language to our problems. If it’s valuable, it will catch on; otherwise, time will pass it by. It will still work for a while probably, even without maintenance. There’s something beautiful about that.

👍 4
emccue01:12:13

Wait, I wrote a for-map today

emccue01:12:29

(defmacro for-map
  "Shortcut for putting the results of a for block into
  a map. Requires that each iteration in the for resolve
  to a vector of two items [key value].

  (for-map [x (range 3)]
    [x (inc x)])
  ;; => {0 1, 1 2, 2 3}

  (for-map [x (range 8)
            :when (even? x)]
    [x (* x x)])
  ;; => {0 0, 2 4, 4 16, 6 36}"
  [bindings & exprs]
  (when-not (vector? bindings)
    (throw (IllegalArgumentException.
             "for-map requires bindings to be in a vector.")))
  (when-not (even? (count bindings))
    (throw (IllegalArgumentException.
             "for-map requires an even number of forms in binding vector.")))

  `(into {}
         (for ~bindings
           (let [result# (do [email protected])]
             (if-not (and (vector? result#) (= (count result#) 2))
               (throw (IllegalArgumentException.
                        (str "The expression in a for-map must evaluate to a vector of two items. "
                             "Got "
                             result#)))
               result#)))))

emccue01:12:06

not even joshing

eccentric J01:12:20

Should I be validating from a macro similarly to yours?

emccue05:12:38

I think validating what you can statically is nice, since errors during macro expansion are annoying. I have one check in there which occurs at runtime, which is not ideal. I am considering maybe restricting the macro even more by making it so that the body of for-map has to be a 2 item vector at macro time

emccue05:12:09

Compare the errors I get with my macro with and without the explicit checks

emccue05:12:21

clojure
;; Without explicit error check
(for-map [a (range 10)]
         (list a (inc a)))
Execution error (ClassCastException) at rubidium.quotes/eval9283 (form-init5967951676652970669.clj:1).
java.lang.Long cannot be cast to java.util.Map$Entry

;; With explicit error check
(for-map [a (range 10)]
         (list a (inc a)))
Execution error (IllegalArgumentException) at rubidium.quotes/eval9476$iter$fn$fn (form-init5967951676652970669.clj:1).
The expression in a for-map must evaluate to a vector of two items. Got (0 1)

eccentric J07:12:32

@U3JH98J4R Thank you, that makes sense, the second one is a lot more clear. I’ll add some similar validation if nothing else but for practice. I take it static validation means before the macro returns a list of expressions?

eccentric J07:12:47

Also, we even used the same examples in our documentation. Craziness.