Fork me on GitHub
#beginners
<
2018-08-07
>
chase-lambert00:08:32

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.

dpsutton00:08:16

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

dpsutton00:08:32

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

chase-lambert00:08:45

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.

chase-lambert00:08:17

(#( % numbers) sum) ?

dpsutton00:08:45

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

chase-lambert00:08:38

yep. but how does map get to that first step

chase-lambert00:08:23

i think i'm overthinking it

noisesmith00:08:23

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

dpsutton00:08:34

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

dpsutton00:08:47

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

chase-lambert00:08:25

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

dpsutton00:08:29

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

dpsutton00:08:45

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

chase-lambert00:08:21

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

chase-lambert00:08:31

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

dpsutton00:08:50

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

seancorfield01:08:17

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.

iagwanderson01:08:21

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”

chase-lambert01:08:23

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

iagwanderson01:08:45

@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.

chase-lambert01:08:15

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

noonian01:08:42

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]))

chase-lambert01:08:30

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.

stardiviner01:08:34

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?

noonian01:08:43

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

noonian01:08:36

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

noonian01:08:47

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

stardiviner01:08:58

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?

noisesmith01:08:38

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

noisesmith01:08:19

also, hi @noonian

noonian01:08:46

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

noonian01:08:28

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

noonian01:08:34

And hi @noisesmith!

stardiviner01:08:25

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

noonian01:08:39

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)

noonian01:08:25

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

stardiviner01:08:59

Oh, I got it. Thanks really helpful.

funyako.funyao15607:08:38

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

ordnungswidrig08:08:27

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

michael.gaare14:08:45

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

henrik14:08:38

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

llsouder16:08:52

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, …}

llsouder17:08:43

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

lilactown17:08:57

@llsouder how are you running the clojurescript code?

andy.fingerhut17:08:59

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.

llsouder17:08:33

@lilactown figwheel, chrome inspector

lilactown17:08:53

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

lilactown17:08:06

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

lilactown17:08:26

changing it to println will print the CLJS data structure correctly

lilactown17:08:36

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

llsouder17:08:58

AH! the println worked!

llsouder17:08:36

is there anyway to access javascript stuff in a repl?

lilactown17:08:48

what do you mean?

llsouder17:08:32

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

lilactown17:08:07

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

lilactown17:08:13

(js/alert "Hello")

lilactown17:08:23

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

llsouder17:08:41

sure, but can I go the other way?

llsouder17:08:01

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

llsouder17:08:16

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

lilactown17:08:29

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

noisesmith17:08:54

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

noisesmith17:08:24

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

llsouder17:08:54

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

llsouder17:08:31

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

llsouder17:08:19

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

jarvinenemil19:08:43

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

jeremy64219:08:38

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?

jeremy64219:08:33

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

michael.gaare20:08:13

@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

jarvinenemil20:08:49

@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.

michael.gaare20:08:33

Joy of Clojure is undoubtedly the better book

takamura.developer22:08:19

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

takamura.developer22:08:31

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

seancorfield23:08:56

@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

takamura.developer23:08:26

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!

sundarj22:08:48

@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

seancorfield23:08:07

@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.

lilactown23:08:08

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

lilactown23:08:12

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

takamura.developer23:08:53

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

funyako.funyao15623:08:54

@ordnungswidrig Alright thanks!

lilactown23:08:23

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

lilactown23:08:45

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

takamura.developer23:08:29

@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?

takamura.developer23:08:31

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

lilactown23:08:21

yeah. although a new one just came out that looks pretty interesting: https://github.com/nubank/workspaces

lilactown23:08:37

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