Fork me on GitHub

Hello, I'm hoping for a little clarification on this example in the Brave book. It's that last anon function that is confusing me. It seems to be making a list of ((sum numbers) (count numbers) (avg numbers)) but I'm not seeing how the map function knows to put each of those functions into the % slot. I'm thinking I need to review anon functions again or something.


#(% numbers) is equivalent to (fn [f] (f numbers))


the % is the argument to the function created the anonymous function syntax there. It "knows" because that's what it does all the time. it's just simple replacement. ((# + 2 %) 3) "knows" to put the 3 in for the % because that's what the form explicitly does


yep. it seems straightforward. so close to clicking! I get the ((# + 2 %) 3) anon syntax but i'm just not seeing the 3) portion in the example i provided to know what to put in the %. i'm trying to write out a step by step breakdown in my notebook.


(#( % numbers) sum) ?


-> ((fn [x] (x numbers)) sum) -> (sum numbers)


yep. but how does map get to that first step


i think i'm overthinking it


(map f [x y z]) produces a lazy-seq with ((f x) (f y) (f z))


map in this simple case takes a function f and a list [ x y z ] and gives you [ (f x) (f y) (f z) ]. I'm using [] so we don't get lost in function application


((sum numbers) (count numbers) (avg numbers))


ok. i think i got it. i knew what it was doing just was missing a step or two as I was trying to run it through in my head. i like to see what happens every single step in the evaluation


and f in this case is (fn [x] (x numbers)). the function which applies its argument to numbers


absolutely. that's how SICP builds it up. it's called the substitution model


yeah and i played around with Racket and their IDE literally has a step by step evaluation tool that was really helpful for this


it's funny i was just discussing slowly adding SICP study (even though it uses scheme) to my Clojure study path with another person the other day. Help me build a stronger fundamental understanding. i just don't want to take away too much from my actual clojure progress


well this chapter is all about that mental expansion of what's going on. SICP is fantastic but isn't necessary for clojure and has a decidedly different style to Clojure. Do it when/if you want but just writing and reading code and playing with the repl will get you going


I think it's quite a big mental step the first time you see a "sequence of functions" rather than a regular "sequence of values" -- but functions are values in FP languages so you can have them in data structures alongside "regular" values, and the various sequence functions (map, filter, reduce, etc) can process sequences of functions just fine.

💯 4
👍 4

funny though. I remember the day I sent a message to one of my friends telling him… “hey… hey, I think I got what all this f-as-first-class-citizen means… Look at this example where you have a ‘list’ of functions instead of ‘list’ of values in the map operator… o.O”


yup. exactly. and I'm basically right at that point in the Brave book. it just kind of gives one sentence on that it is a neat capability and moves on but i'm not quite that quick on the pickup. And as I tried to break it down and experiment in the repl I think I overcomplicated it in my head trying to go step by step


@chase-lambert what has been very helpful for me is to work on books & repl; practice on 4clojure website and start a small project by yourself.


that is good to hear because it's almost word for word what i've settled into!


It might be easier to understand if you remove the shorthand anonymous function syntax, and define numbers at the top level so you can understand what’s going on with a concrete example and don’t have to worry about passing numbers in to the function in map. Hopefully that would clarify at least where the misunderstanding lies:

(def sum #(reduce + %))
(def avg #(/ (sum %) (count %)))
(def numbers [5 10 15])

;; What `#(% numbers)` did in the original example
(defn call-with-numbers [f]
  (f numbers))

(def stats (map call-with-numbers [sum count avg]))


ok yes. this is what i was getting at. i could see what the end result of everything was doing. it was when I tried to make sure I truly understood how it actually did it by breaking it down to every single step that I confused myself. I think just taking the #(% numbers) and expanding that to the (fn [f] (f numbers)) was really helpful.

👍 4

What does the ->Account mean? I checked cider-doc on it, it means positional of class user.Account. Is it a syntax sugar or a Clojure reader syntax like #' etc?


defrecord creates that constructor function for you as well a map->Account constructor that takes a single map of the fields as an argument and returns an instance of Account


The ->SomeRecord version takes the fields in the order they are defined in defrecord e.g. (->Account id balance)


They are just normal function names and not special syntax like #'


I have a little understand now, Is this ->SomeRecord is a constructor function? And this kind of format ->SomeRecord is specific to defrecord or also can be seen in Clojure other places?


deftype creates something similar as well, but those are the only ones I know of


defrecord is a macro that does a few things. It 1) defines a new type with deftype and implements some core protocols, and then 2) it defines two functions that return instances of that type, one which is called ->SomeRecord and one called map->SomeRecord. So defrecord is a little special because it’s a macro, but those functions that are created are just normal functions


You can actually see what defrecord does by macroexpanding it, although its a little messy


@noisesmith @noonian Thanks. I will check out that macroexpanding result.


It outputs a lot of code! Here’s a stripped down version where I removed the implementations in deftype:

(macroexpand-1 '(defrecord Account [id balance]))
  (clojure.core/declare ->Account)
  (clojure.core/declare map->Account)
  (deftype* user/Account user.Account
    ;; ... Elided code
  (clojure.core/import user.Account)
    "Positional factory function for class user.Account."
    [id balance]
    (new user.Account id balance))
    "Factory function for class user.Account, taking a map of keywords to field values."
        (clojure.core/into {} m__7585__auto__)))))


I wouldn’t worry about what the generated code is doing, but you can see that at a high level all it is doing is using defn to define both of those functions


Oh, I got it. Thanks really helpful.

👍 4

What is the goto library for rest api? yada or liberator?


@funyako.funyao156 both are fine. Liberator runs on any ring compatible server where yada requires you to use aleph as the http server.


depending on how rest-y you’re going there’s also compojure and compojure-api


For the longest time, I just sent EDN over a websocket, because that was more intuitive to me than REST. When I eventually created my first REST API (to be consumed by someone else) it was with compojure-api. That worked well.


in my repl I get this:

dev:cljs.user=> (def newstuff "hello")
dev:cljs.user=> (type newstuff)
dev:cljs.user=> (map clojure.string/capitalize newstuff)
("H" "E" "L" "L" "O")
But in clojurescript
(let [newstuff "hello"]
  (js/console.log (type newstuff))
  (js/console.log (map clojure.string/capitalize newstuff))

I get
ƒ String() { [native code] }
 cljs.core.LazySeq {meta: null, fn: ƒ, s: null, __hash: null, cljs$lang$protocol_mask$partition0$: 32374988, …}


I can see they are different types of strings, java and JavaScript. I thought clojure.string functions worked on JavaScript types.


@llsouder how are you running the clojurescript code?


In the Clojure case, you are actually passing individual characters, not strings to clojure.string/capitalize, which you can see by trying out (map identity "hello") there. clojure.string/capitalize does not give an error when given a Java char as an argument, but treats it kind of like a one-character string.


@lilactown figwheel, chrome inspector


js/console.log doesn't know how to handle CLJS data structures by default


cljs.user=> (let [newstuff "hello"]
       #_=>   (js/console.log (type newstuff))
       #_=>   (println (map clojure.string/capitalize newstuff)))
{ [Function: String] closure_uid_797408116: 7 }
(H E L L O)


changing it to println will print the CLJS data structure correctly


there's a Chrome plugin that enables js/console.log to pretty-print and properly inspect CLJS data structures:


AH! the println worked!


is there anyway to access javascript stuff in a repl?


what do you mean?


Is the state of the browser passed back to the repl?


if you have a REPL connected to your browser, your REPL code is run directly on the page


(js/alert "Hello")


should show an alert dialog in the browser if you're connected


sure, but can I go the other way?


say if I type something into a text box which changes an atom


can I get that atom's state in the repl?


yep, all of your REPL code is run in the page


as long as the atom has a scope where you can access it


this is a standard way to do cljs dev, try something in the repl, see what's in your state atom, etc.


I am loading a text file into an atom called \@file-data in a ns called data-project.upload


so changed to that name space and (println @file-data)


doh! that totally worked. THANKS everyone! I am back on track.


Anyone with a good tutorial on Datomic? I've read the official one and it is ok. Want some more though


I have a weird issue where a fn I call is not getting a param passed to it. It gets passed as null. Even though I see using a spy macro and the debugger that a value is passed. Any thoughts on what I might keep an eye out for to debug this?


Nevermind that. It seems it's just the debugger being confused. It actually does get the value and returns the correct value.


@coderdanger I think the one in this book is pretty good, although I’m biased since I wrote it.


@michael.gaare Thank you I will have a look, trying to get my hands on as many good Clojure books I can find to be honest. Gotta finish "Joy of Clojure" first though.


Joy of Clojure is undoubtedly the better book


What do you recommend for using React in clojurescript? Reagente, Om now or OmNext?


Also, using clojurescript for react makes me lose (or make harder) access to other libraries like storybook, relay, redux, etc?


@takamura.developer Based on my experiences with cljs/react (which are limited and a bit out of date now), I'd recommend starting with reagent/re-frame and see how you get on with those. I can't speak to those other libraries (but I believe re-frame is intended to bring some of that functionality to reagent?). You may need to ask for more details in #re-frame #reagent #clojurescript


Thanks for your reply @seancorfield! I read the reagent documentation and it seems to be easier/more intuitive than om/om next. But I will try to find more about the topic. Thanks!


@takamura.developer there's also #fulcro, which is a batteries-included, easier to get started with, better documented fork of Om Next. there are videos, and a detailed book. the author is also active in slack, should you need to ask any questions.


@takamura.developer Back when we built some proof of concept stuff in cljs at work, we used Om first and then we rewrote it all in Reagent, to get a solid feel for how both compared. Om felt very "object-oriented" to us whereas Reagent felt a lot more functional. Your mileage may vary of course.


we’ve used storybook at work with reagent. we currently also use apollo-client


apollo-client took some work to make it work within reagent, we decided to skip apollo-react and write our own wrapper for it. I hope we can open source it


@lilactown and was it easy to add the storybook functionality?


you can use reagent.core/as-element to turn your reagent components into pure React components and pass it to the .add method that a story has


although depending on what you want storybook for, you might find that devcards suits your needs more


@seancorfield hmmm, nice point of view. I did some tutorials using om next and what was hard for me was having to use new concepts like queries, mutations, etc. But I thought that after some time, it would pay off. Reagent lack all of this, right?


@lilactown Thanks! I will check this repo. Also I should check devcards :thinking_face:, it is more clojure-friendly, right?


yeah. although a new one just came out that looks pretty interesting:


if you want the well-traveled path, devcards is quite nice still