This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-08-07
Channels
- # beginners (95)
- # cider (131)
- # cljdoc (12)
- # cljsjs (2)
- # clojure (209)
- # clojure-dev (1)
- # clojure-italy (3)
- # clojure-nl (10)
- # clojure-russia (37)
- # clojure-spec (19)
- # clojure-uk (182)
- # clojurescript (136)
- # cursive (28)
- # datomic (28)
- # editors (55)
- # figwheel-main (1)
- # fulcro (36)
- # hyperfiddle (12)
- # jobs (1)
- # jobs-discuss (55)
- # luminus (1)
- # mount (1)
- # off-topic (28)
- # onyx (18)
- # reagent (17)
- # ring-swagger (4)
- # rum (14)
- # shadow-cljs (22)
- # spacemacs (15)
- # tools-deps (16)
- # vim (26)
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.
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.
(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
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
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.
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.
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.
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)
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
also, hi @noonian
@noie
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
And hi @noisesmith!
@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]))
Gives
(clojure.core/let
[]
(clojure.core/declare ->Account)
(clojure.core/declare map->Account)
(deftype* user/Account user.Account
;; ... Elided code
)
(clojure.core/import user.Account)
(clojure.core/defn
->Account
"Positional factory function for class user.Account."
[id balance]
(new user.Account id balance))
(clojure.core/defn
map->Account
"Factory function for class user.Account, taking a map of keywords to field values."
([m__7585__auto__]
(user.Account/create
(if
(clojure.core/instance?
clojure.lang.MapEquivalence
m__7585__auto__)
m__7585__auto__
(clojure.core/into {} m__7585__auto__)))))
user.Account)
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
@funyako.funyao156 both are fine. Liberator runs on any ring compatible server where yada requires you to use aleph
as the http server.
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. https://github.com/metosin/compojure-api
in my repl I get this:
dev:cljs.user=> (def newstuff "hello")
#'cljs.user/newstuff
dev:cljs.user=> (type newstuff)
#object[String]
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.
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
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)
nil
there's a Chrome plugin that enables js/console.log to pretty-print and properly inspect CLJS data structures: https://github.com/binaryage/cljs-devtools
if you have a REPL connected to your browser, your REPL code is run directly on 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
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. https://www.amazon.com/Professional-Clojure-Jeremy-Anderson/dp/1119267277
@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.
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. http://fulcro.fulcrologic.com
@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.
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?
@ordnungswidrig Alright thanks!
yep. I pretty much followed https://github.com/shadow-cljs/examples/tree/master/cljs-storybook
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: https://github.com/nubank/workspaces