Fork me on GitHub
#beginners
<
2016-02-06
>
harrigan11:02:17

The second paragraph is confusing me: Java also includes the classes java.lang.BigInteger and java.lang.BigDecimal for arbitrary precision integer and decimal values, respectively. The Clojure arithmetic operations intelligently return these kinds of values as necessary to ensure the results are always fully precise, e.g. adding the Integer values 2000000000 and 1000000000 will return the value 3000000000 as a BigInteger because it is too big to be an Integer.

harrigan11:02:53

It returns a Long instead of a BigInteger. And, in any case, should be references to BigInteger be references to BigInt instead?

timaeus12:02:01

hello, small question: if one tries to access a map called body {"name":"Cheshire Cat","status":"grinning”} in a .cljs file, how would you do that? I’ve tried (:name body) but it returns null

timaeus12:02:45

the only way i have gotten it to work is to define the body differently in the let: let body (.parse js/JSON :original-body) and then use a js method when accessing the value: (.-name body).. which seems a ineffective.. any suggestions?

akiva13:02:00

@timaeus, that’s correct. What you have is JSON rather than a map. You’ll want to use clojure.data.json/read-str “{\”name\”:\"Cheshire Cat\”,\”status\”:\”grinning\”}” :key-fn keyword) to get a Clojure map with keywords like you’re expecting.

akiva13:02:06

Personally, any time I’m dealing with JSON->map/map->JSON, I use Cheshire: https://github.com/dakrone/cheshire

timaeus13:02:52

yes i tried to include cheshire in the cljs but i actually didn’t get it to work either

akiva13:02:41

Which error was it giving you?

timaeus13:02:25

first, i simply required cheshire, after which the cljsbuild output told me that cheshire should be required under the :require-macros key

timaeus13:02:44

then i did as so: :require-macros [cheshire.core :as json]

timaeus13:02:12

which returned a ‘json not defined’ error when i tried to use a method from cheshire

timaeus13:02:56

with WARNING: No such namespace: json at

akiva13:02:51

Ah, that’s because Cheshire doesn’t support ClojureScript.

akiva13:02:18

You might want to look into cljs-ajax which bakes in JSON->map coercions.

djtango14:02:32

Hey all, I'm a beginner and am trying to find out something that I'm struggling to look up, wondering if someone can explain if what I am trying to achieve is possible. If I have a function that binds some value to a symbol, e.g.:

(defn some-function [f]
  (let [x 5]
    (f)))
Is there a way to pass in something to some-function that references the locally bound variable? E.g.
(some-function #(* x x))
;;=> 25
I realise that I could adapt these to be like this:
(defn some-function2 [f]
  (let [x 5]
    (arg 5)))

(some-function2 #(* % %))
;;=> 25
So this is more a conceptual question of whether the first example is possible? It relates to the design of a function I have, I would like the flexibility of being able to reference symbols defined within the let without being bound (pun unintended) in the form of what I can pass into the function.

djtango14:02:50

I have tried playing with x and 'x. I have also tried playing with (delay ...) to get the code passed in to be evaluated only from within some-function

akiva15:02:27

@djtango, it behaves as a closure but you’d have to pass x in explicitly as you’ve already demonstrated; when you define a function such as #(* x x) it tries to resolve x immediately from the current closure.

akiva15:02:52

Only way you can do this without it trying to resolve vars is with a macro.

djtango15:02:11

okay @akiva thanks for the advice - will look up macros!

akiva15:02:23

Good luck. A bit more advice: I always try to find a way to solve a problem without using macros. They’re very powerful and thus easily abused and overused when a non-macro solution probably exists such as using partial. For instance, you could probably change the above to pass in a function that takes two arguments and then do (partial f x).

akiva15:02:27

Or even ((f x)) and pass #(* % %) to it or whatever.

isaac_cambron17:02:55

@djtango, @akiva : It’s not usually good idea, but what you seem to be looking for is dynamic scoping which Clojure does support:

user=> (def ^:dynamic x 2)
#'user/x
user=> (defn double-x [] (* 2 x))
#'user/double-x
user=> (double-x)
4
user=> (defn some-function [f] (binding [x 3] (f)))
#'user/some-function
user=> (some-function double-x)
6

djtango18:02:32

@isaac_cambron: thanks for this, I am reading up on ^:dynamic and binding now and will try to make sense of whether I should be using a macro or binding for my use-case or if I'm even thinking about my use-case in the right way at all. Would it be okay to DM you if I have further questions?

isaac_cambron18:02:21

I’d prefer we just chat here, since if I’m not around, someone else can jump in

isaac_cambron18:02:49

Macros can basically do anything

isaac_cambron18:02:03

but they’re often hard to understand and debug

isaac_cambron18:02:13

so it really comes down to the details of the real problem you’re solving

isaac_cambron18:02:09

I strongly suspect that if you can’t solve the problem you have by just passing in what you need, that dynamic binding would be better than a macro

isaac_cambron18:02:17

If you have a situation where you have a bunch of default values of parameters that you may or may not want to override in the caller, I’d suggest parameterizing it with a destructured map with defaults

isaac_cambron18:02:29

and just building the map with what you need

isaac_cambron18:02:15

Like this:

user=> (defn foo [{:keys [a b c] :or {a 1 b 2 c 3}}] (println a b c))
#'user/foo
user=> (foo {})
1 2 3
nil
user=> (foo {:a 5 :c 9})
5 2 9
nil

isaac_cambron18:02:53

But I don’t know if that maps cleanly to the problem you’re solving

djtango18:02:01

okay so the use case is that I am using data from a SQL database to populate an HTML table on a webpage. I am using a recursive function to populate the table, but in order to keep the function generic (so the same function can be used elsewhere) I have allowed for additional arguments so that the table can support additional fields.

djtango18:02:11

(defn gen-order-rows
  [order-rows order-data & [extra-html]]
  (let [current-row (first order-data)]
    (if (= order-data '())
      order-rows
      (gen-order-rows (conj order-rows [:tr [:td (str (current-row :name))]
                                        [:td (str (current-row :quantity))]
                                        (if extra-html
                                          (extra-html current-row))])
                      (rest order-data)
                      extra-html))))

isaac_cambron18:02:23

here’s my example rewritten in the way i’m suggesting:

user=> (defn double-x ([] (double-x {})) ([{:keys [x] :or {x 1}}] (* x 2)))
4#'user/double-x
user=> (double-x)
2
user=> (defn some-function [f] (f {:x 3}))
#'user/some-function
user=> (some-function double-x)
6

isaac_cambron18:02:00

yup, with you so far

djtango18:02:22

That's the sample code. The line (extra-html current-row) is the line I'm talking about. The data passed in is a sequence of maps. So I'd like to be able to pass in keys to the map to unpack data for the current-row.

isaac_cambron18:02:29

yup. and i suppose that if the caller wants extra-html, they really need to provide those keys?

isaac_cambron18:02:41

i.e. there’s no default set of them

djtango18:02:16

so in order to get it working, I've chosen to pass in extra-html as

(fn [row] [:td (:subtotal row)])
But in the original conception, I had hoped to simply pass in something along the lines of
[:td (:subtotal current-row]

djtango18:02:02

The idea was to keep it flexible, but I could include :subtotal in the if clause

djtango18:02:57

so the basis of my questioning was to further my understanding of clojure by asking a more a general conceptual question of how could I achieve xyz rather than how to implement this feature, if that makes sense

isaac_cambron18:02:00

I think if you want to get that just right, a macro would be the way. But honestly, I think the (fn [row] thing that you have is best

isaac_cambron18:02:03

Even philosophically, having a function where you need to do a potentially arbitrary thing is the whole point of functions as first-class citizens

isaac_cambron18:02:19

and it will give you a ton of flexibility

isaac_cambron18:02:53

for example, if you find ourself making a lot of simple table cells, you can just (defn simple-td [key] (fn [row] [:td (get row key)]))

isaac_cambron18:02:02

and then pass in (simple-td :subtotal)

isaac_cambron18:02:43

i.e. functions are really what get you the most composability

isaac_cambron18:02:49

(sorry, had to fix that a couple times)

djtango18:02:16

whoa that's really cool

djtango18:02:37

it's so simple but I can see how you can grow that

djtango18:02:40

thank you! 😄

isaac_cambron18:02:09

You’re totally welcome. That “oh, wow, I can build arbitrarily complicated things by composing simple functions!” realization you just had is one of the core things people love about functional programming. It’s fun to watch someone have it.

akiva18:02:44

[applauds]

Mikko Koski19:02:47

What's the difference between (get map key) and (find map key) or are there any?

Mikko Koski19:02:13

Oh, now I see it...

Mikko Koski19:02:36

Answering to myself: find returns key value pair.