Fork me on GitHub
#beginners
<
2018-10-05
>
cybersapiens9703:10:20

Why does Reduce works when you don't pass a base case argument?

cybersapiens9703:10:36

what becomes the base case when this happens

seancorfield04:10:53

@cybersapiens97 The first element of the collection. The docstring explains how things work fairly explicitly.

seancorfield04:10:39

([f coll] [f val coll])
  f should be a function of 2 arguments. If val is not supplied,
  returns the result of applying f to the first 2 items in coll, then
  applying f to that result and the 3rd item, etc. If coll contains no
  items, f must accept no arguments as well, and reduce returns the
  result of calling f with no arguments.  If coll has only 1 item, it
  is returned and f is not called.  If val is supplied, returns the
  result of applying f to val and the first item in coll, then
  applying f to that result and the 2nd item, etc. If coll contains no
  items, returns val and f is not called.

seancorfield04:10:05

So (reduce f [a b]) is (f a b), (reduce f []) is (f), and (reduce f [a]) is a.

seancorfield04:10:46

That middle one is what often catches people out.

seancorfield04:10:36

With an initial val, those three cases become (reduce f i [a b]) => (f (f i a) b), (reduce f i []) => i, and (reduce f i [a]) => (f i a)

cybersapiens9704:10:53

Thanks! i'll get into the habit of (doc fn) 🙂

cybersapiens9704:10:00

Veterans of Clojure, in real world scenarios, how often you have to use Generative Recursion? Or it really depends on the kind of problem you’re tackling ?

seancorfield04:10:41

The REPL gives you both doc and source!

seancorfield04:10:00

(and apropos when you're not quite what you're looking for)

cybersapiens9704:10:38

Nice, didn’t know about `source

seancorfield04:10:56

Regarding Generative Recursion, I doubt most Clojurians even bother considering whether they're doing structural recursion or generative recursion -- it's going to depend on the shape of the problem.

seancorfield04:10:23

A lot of the strictly recursive solutions from the Lisp world tend to be addressed by map/reduce as well. Or some other higher-order approach.

cybersapiens9704:10:32

Wow, because it seems to me a totally different approach when dealing with non Structural Recursion. Thinking in terms of an algorithm, breaking the problem down into smaller easier solvable problems calls for some insight and having to warn or prevent the user for potential harmful inputs (non-termination)

cybersapiens9704:10:14

But maybe that’s only for specific hard problems...

seancorfield04:10:10

Remember that most Clojurians are very pragmatic about just getting sh*t done. There are definitely some folks who think about academic nuances of algorithms but that's likely a minority here overall.

cybersapiens9704:10:41

I wonder if this is bad for me, or if this is something I should stick with. Sometimes I feel I’m overthinking the problem or achieving the same results from people who just don’t pay attention to those things and do it faster...

seancorfield04:10:00

As I recall, you pushed back when I suggested you really try think in terms of Clojure's abstractions -- as opposed to trying to think in terms of Lisp-like manipulation of concrete data structures...?

seancorfield04:10:20

Whilst Clojure "is a" Lisp, Clojure != Lisp.

cybersapiens9704:10:10

Hmm, I got it, I mean, I’ve spent a lot of time working on the HTDP book, (like a couple of months) and their recipe for designing programs just got stuck deeply in my mind, and their language didn’t really had a lot of abstractions, so I need time for a adapted mindset towards Clojure. For example, they enforce the specification of information as Data, for every function or problem you have to solve, and now I’m eager to work with Spec for doing that...

seancorfield04:10:26

HTDP is good stuff. Gregor Kiczales online course based on it is good too. And I followed those practices for a while with Clojure as an experiment.

seancorfield04:10:25

(and that was before spec 🙂 )

seancorfield04:10:01

But there are definitely some gaps between the HTDP approach and what is idiomatic Clojure -- and a fluid Clojure workflow.

cybersapiens9705:10:06

It was a great way of learning and understanding Functional Programming and also designing programs... it gave me a lot of insight about programming and computer science. And I think their approach (the complete recipe) still valid when I’m stucked, because it really helps to avoid looking at the screen and not really knowing the next step. I guess I’ll have to take the habit of program more and more in terms of CLJ abstractions, because their meant to make us productive right? Learning the behind the scenes is good for understanding the process but thinking about it while coding, seems unproductive, and I’m so hard stucked on their approach, that I have to think how the problem is solvable without using a high order function, so then I can translate/simplify the code using abstractions. That’s really slow, but in overall I’m enjoying my experience with Clojure for sure!

seancorfield05:10:40

Yeah, that's one of the great things about HTDP: when you're stuck, it provides a good roadmap to figure out the little steps along the way.

seancorfield05:10:03

I try hard to avoid thinking too much about the concrete behind the scenes stuff unless I'm not able to understand the behavior of something based purely on the abstractions. It's a shift worth making if you can.

seancorfield05:10:36

Some of it only comes with practice and familiarity I suspect tho'...

seancorfield05:10:29

Clojure is the most fun language I've ever used -- and I've used a lot over nearly four decades 🙂

seancorfield05:10:38

How To Design Programs @nate_clojurians

cybersapiens9705:10:28

Thanks for the great insights @seancorfield it’s nice and a relief to hear those things from a experienced person :)

seancorfield05:10:23

FWIW, I sort of started with Lisp (1982/83) at university when my best friend decided to build a Lisp interpreter as his final year project.

seancorfield05:10:47

(my final year project was to build an APL interpreter -- but I helped him test his project)

cybersapiens9705:10:07

How nice, it seems to me that the Lisp approach to Programming is awesome for learning... wish they used it along the HTDP approach in the universities here at Brazil. They mostly use C, and you kinda learn to mimic your professor, that’s all

seancorfield05:10:18

My university course focused on Pascal and assembler for learning 🙂

seancorfield05:10:20

I learned about a dozen other languages on my own at university -- the computer lab was very tolerant of me borrowing every manual I could get my hands on 🙂

cybersapiens9705:10:32

Hahah seems fun times

cybersapiens9705:10:23

Btw, after all those years, do you think FP is a better approach towards the market problems against OOP? Or it really depends on the specific problem

seancorfield05:10:37

Oh, I've always felt FP is the better approach!

seancorfield05:10:06

After I graduated in '83, I did three years postgrad research into FP language design and implementation.

seancorfield05:10:43

When Haskell came out, I was convinced it would storm the world (so of course I was very disappointed that the Haskell community seemed determined to avoid success at all costs!).

seancorfield05:10:29

I learned OOP purely because that's what the market demanded but I did C/assembler mostly from 86 to 92, then C++ to 97, then Java.

andy.fingerhut05:10:49

Do you mean some particular kind of problems when you say "market problems against OOP"?

seancorfield05:10:54

I was on the C++ Standards Committee for eight years in the 90's 🙂

cybersapiens9705:10:17

But I don’t get why OOP become so popular if FP was so much cleaner and easier to maintain

andy.fingerhut05:10:59

Computers are much much cheaper per Gbyte of RAM and GHz of processing power than they were 20-30 years ago. That makes a difference.

cybersapiens9705:10:13

@andy.fingerhut not really, but I’m focused on web development, though I’ve never did code professionally, but I plan to use Clojure as back end for some projects of mine

andy.fingerhut05:10:59

Even in the 1980's a few software developers argued against C as being too expensive, vs. assembler.

cybersapiens9705:10:20

Seems fair, so performance was a much higher problem back then

seancorfield05:10:29

OOP was sold as "modeling the real world" at a time when FP was still considered academic and requiring specialized and/or high-powered computers.

andy.fingerhut05:10:16

$2500 in 1980 bought an Apple II with 48Kbytes of RAM, 144 Kbyte floppy disks, no hard drive, 1 MHz CPU.

andy.fingerhut05:10:56

I double checked my Ks and Ms there. No typos 🙂

cybersapiens9705:10:48

Do you guys have any books or resources, for someone who is going to start it’s own journey into web development, but as a company? Idk where to start when the subject is production methodology... and I don’t want to start as “just do it”

seancorfield05:10:18

My first serious computer was a SAGE IV with 1Mb of RAM (a giant circuit board covered in 8Kb chips!) and a 20Mb HD.

seancorfield05:10:31

(and 5.25" floppy drive)

cybersapiens9705:10:53

@andy.fingerhut that’s insane, I almost cry when I see the big gap we made compared to the beginning of computers haha

seancorfield05:10:07

@cybersapiens97 Not quite sure what you're asking there...

andy.fingerhut05:10:34

I have probably heard of such a book, but not read one myself, so no personal knowledge to recommend for or against any books there.

cybersapiens9705:10:16

I don’t have any professional experience, so I roughly know how software production works, I don’t know if I’m using the right therm (methodology). I want to know, how to approach the product production phase properly

andy.fingerhut05:10:30

There is a book "Ship it!" I've heard of, that is in that area -- reviews on Amazon, but first published 2005 and not sure whether it was written with enduring advice or short shelf life advice.

cybersapiens9705:10:10

I’ll check it out and ask around, thanks

borkdude08:10:45

@cybersapiens97 what about Release it! by @mtnygard?

gklijs09:10:14

@cybersapiens97 You might like ‘the phoenix project’ it’s setup as a novel, and about moving towards devops in an organisation.

peter.kehl15:10:58

https://clojure.org/guides/weird_characters#__code_code_reader_eval has examples that fail:

#="foo"
;...
(def foo 1)
#='foo
Both fail (with either CLJ 1.9.0 and CLJ 1.10alpha8): Syntax error reading source at (XX:0). Cause: Unsupported #= form Any tips, please?

azzurite15:10:24

is there a difference between (-> states first) and (first states)? no right? should the first ever be used?

trailcapital15:10:53

(-> states first) literally becomes (first states) at compile time

trailcapital15:10:02

user=> (macroexpand '(-> [1 2 3] first))
(first [1 2 3])

azzurite15:10:35

I know I just wondered if there maybe still is some reason to use (-> states first), in a macro or sth?

bronsa16:10:05

@peter.kehl the docs are wrong, those never worked

peter.kehl16:10:17

@bronsa Thank you. Is there anything similar that works (is it a typo, or misunderstood/misinterpreted functionality), or are those two examples completely wrong?

bronsa16:10:26

they're completely wrong

bronsa16:10:26

#= is mostly an internal implementation and the set of features it supports are not documented and very limited

peter.kehl16:10:01

@bronsa Thank you. I'll file an issue (if there isn't one yet).

peter.kehl16:10:20

@azzurite What's sth, please?

dadair16:10:44

“something” shorthand

noisesmith16:10:46

in my experience it's relatively common to see (-> x f) in production code; not totally sure if it's because people are in love with the arrow or maybe they were planning on adding more steps (or once had more steps that were factored out)

noisesmith16:10:04

luckily, (-> x) never shows up (it's just as valid, and make as much sense)

joelsanchez17:10:16

too many times I've done (map fn coll) only to have to refactor it as (->> coll (map fn)) because I need to do something else

joelsanchez17:10:31

sometimes I prefer to have a ->> than to waste time on that

noisesmith17:10:47

with the right editor a change like that is nearly instant

peter.kehl18:10:21

@noisesmith What editor(s) support such refactoring?

noisesmith18:10:34

emacs with paredit, or vim with paredit or vim-sexp both make it straightforward - I wouldn't be surprised to hear about other similar tools

dpsutton18:10:07

clojure-mode for emacs has lots of structural refactorings like this

seancorfield18:10:07

convolute-sexp I think?

seancorfield18:10:10

Put the cursor here (map fn |coll) and convolute-sexp turns it into (coll (map fn)) and you can just insert ->> (I believe it leaves the cursor in front of coll? It's been a while since I used that in Emacs).

seancorfield18:10:50

(paredit in Atom doesn't have that function 😞 )

peter.kehl18:10:18

Thank you for editor tips.

peter.kehl18:10:31

On macros: A general rule is to crate a function instead of a macro, if possible. Does def have any different effect in (code generated by) a macro and in a function, please? For example:

(def i 0)
(defmacro i-inc []   '(def i (inc i)))
(defn     i-inc-fn [] (def i (inc i)))

seancorfield18:10:39

Macros expand before code executes. Function bodies run only when called. But it's the same def in both cases.

seancorfield18:10:07

Bear in mind that it always defines a top-level (global) var.

noisesmith18:10:49

yeah, def inside a function is rarely what one actually wants

seancorfield18:10:52

(and you should never see code that uses def multiple times on the same var name)

noisesmith18:10:29

the only time I ever use def inside some other form is for debugging, as it's more useful than just a println sometimes

noisesmith18:10:54

but I'd never check that into git :D

noisesmith18:10:37

and even then, an atom plus swap! is more useful than a def inside a function

seancorfield18:10:24

For the above case, you'd either use an atom:

(def i (atom 0))
(defn i-inc-fn [] (swap! i inc))
;; and then use @i to get the current value
or you'd use alter-var-root (shudder)
(def i 0)
(defn i-inc-fn [] (alter-var-root #'i inc))
;; and you can use plain ol' i to get the current value
But, really, don't do this 🙂

peter.kehl19:10:39

Thank you. Yes, it's for a tool to help with debugging. It will be debug/log indentation, hence thread-local. I guess I'll create it with def :^dynamic, then (binding [var new-value] ...) or def ^:dynamic again.

noisesmith20:10:23

you could do something like (binding [*debug-print-indent* (inc *debug-print-indent*)] ....)

noisesmith20:10:40

(or of course a macro that does that)

peter.kehl20:10:56

@noisesmith I tried to. If I want to keep the value at the top level, how do I define the variable? The following fails:

(def i 0)
(binding [i 2] i)
>> Can't dynamically bind non-dynamic var: user/i

noisesmith20:10:56

because binding only works for something declared ^:dynamic

noisesmith20:10:32

I meant for my suggestion to follow on your idea of def ^:dynamic then binding

noisesmith20:10:09

user=> (def ^:dynamic *foo-level* 0)
#'user/*foo-level*
user=> (defmacro more-foo [& body] `(binding [*foo-level* (inc *foo-level*)] [email protected]))
#'user/more-foo
user=> (more-foo (println "foo " *foo-level*) (more-foo (println "deeper" *foo-level*)))
foo  1
deeper 2
nil
@peter.kehl

jaihindh.reddy20:10:14

If I add a Clojure library as a dependency using a git coordinate in my deps.edn, and that library uses lein and its project.clj, how are (if at all) its dependencies resolved?

gklijs21:10:14

I'm almost certain lein always generates a pom.xml which will be used to get the dependencies

gklijs21:10:25

But I'm not sure, I don't think it's really meant to be used like that. Any rebase/deletion of branch and it's gone, which is fine for testing. I sort of got the idea that created deps.edn with Ions in mind, and for that it's a great way to try some things out, without having to go to the usual release steps, and then pulling it Nexus/maven central

gklijs21:10:01

Just like you can't have dependencies on snapshots for maven releases, but I'm not sure it's impossible

seancorfield00:10:33

Currently tools.deps does not recognize project.clj for dependencies in a Git (or local) repo -- so you can't do that.

seancorfield00:10:05

As @ hints tho', lein could generate pom.xml which, if checked into Git, would allow you to depend on it (maybe). You'd be better off asking the library maintainer to add deps.edn. Or you could fork the library and do either of those yourself @...

blance21:10:17

Hello, recently i find myself having lots of do block only because i need to log something. for example:

(if true 
       (log "something") 
       (do 
          (log "something else")
          (process-data)))
wondering if there's any good way to deal with logging cleanly? this kind of make a otherwise clean code messy..

scot-brown23:10:46

There's nothing wrong with that do block IMO. it doesn't look messy. If you must, you could either wrap your process-data function in a process-data-and-log function. What would be your ideal way of writing code with this kind of behaviour?

lee.justin.m00:10:43

There are spy macros that will log the form, its value and then return the value. I find that very useful especially with a good editor. You just pop in the spy and then take it out when you are done. I’m on mobile so can’t look for one right now

lee.justin.m00:10:01

I think one library is literally called spy

lee.justin.m00:10:25

I wrote my own because I had issues getting it to work in cljs

blance18:10:14

@ I think your suggestion to have log inside of process-data would make it looks nicer. thanks!

peter.kehl22:10:37

@noisesmith Thank you.