programming-beginners

2018-03-13T08:35:21.000201Z

Thank you @jr0cket! That’s a great example (and much easier to understand when you type all the words out, so thank you for that too! 😁)

2018-03-13T11:42:17.000359Z

I read something yesterday about someone creating a Rails-like stack for Clojure, but that it would be unpopular - can anyone give me more context on what this is and why there might be pushback on it?

2018-03-13T11:42:22.000362Z

I believe it was this: http://coastonclojure.com/

practicalli-john 2018-03-13T11:55:42.000014Z

@amelia The Clojure community tends to prefer combining small, focused libraries that do one thing well. This keeps the design quite simple and gives a lot of flexibility In general frameworks tend to encourage / force a particular design. So with a framework, many design decisions have been made for you. Not all those decisions may fit your needs. Frameworks can be great for getting something done quickly, once you have become familiar with them. However frameworks also grow and change and can leave you stuck or having to relearn a framework. The same think can happen with a library, but as libraries are smaller it's usually easier to swap them for another library.

daveliepmann 2018-03-13T11:56:42.000344Z

By default, the Clojure community leans towards libraries rather than frameworks. The distinction between those two is fuzzy. The idea is that libraries are more single-purpose and programmers use them like an individual tool or building block, forming it into the developer's larger solution. Frameworks are more of a comprehensive solution that tries to solve all the problems you would expect to have, at the cost of being more rigid. This makes frameworks more of a pre-existing solution that the developer conforms to, rather than the other way around. Many folks feel that web development is well-suited to framework approaches, since the solution so often has the same basic shape.

daveliepmann 2018-03-13T11:57:49.000292Z

Rails is the epitome of a framework approach. The Clojure community's default preference for libraries means that many Rails devs are lost when they try to architect a solution without a framework.

daveliepmann 2018-03-13T11:59:20.000486Z

Rails' "convention over configuration" philosophy is a key differentiator

practicalli-john 2018-03-13T12:00:29.000537Z

In Clojure there are a few good frameworks, eg. Pedestal, Arachne, re-frame. I tend to use frameworks as examples of how I could build something similar. There are also templates for Leiningen that give you a head start, by creating a project with libraries and example code (composure, luminous).

daveliepmann 2018-03-13T12:01:30.000325Z

That's an excellent point—one of the ways many Clojure devs accomplish similar goals of frameworks is with lein templates like Luminus.

2018-03-13T12:02:31.000058Z

Thank you both! That makes it much clearer.

2
practicalli-john 2018-03-13T12:06:47.000352Z

There does seem to be more Clojure frameworks coming out, which I think is a good thing for Clojure overall. There is also the concept of micro-frameworks which are just frameworks that try to be quite specific (and not get too bloated).

2018-03-13T12:08:11.000141Z

I like the idea of being able to make a web app quickly as a way to get started, though at some point I’d want to learn how each moving part works and how it all fits together.

1
sundarj 2018-03-13T12:09:48.000390Z

a framework is essentially the inverse approach to solving a problem to a library. when solving a problem with a framework, you generally must think "how can i solve this problem in this particular framework" when solving a problem with libraries, you generally think "what library/ies can i pick in order to help me solve it"

2018-03-13T12:12:09.000101Z

I just realised I don’t know what a library is! I’ve been sort of nodding along, but I couldn’t define it if you asked me to. Is it like the clojure.core/clojure.string/clojure.set collections of functions?

daveliepmann 2018-03-13T12:12:16.000536Z

Both approaches have merit. For me, the concern with frameworks is that one still has to learn a great deal about how the framework works, which is non-portable knowledge. Usually what one learns to get similar functionality using libraries is knowledge that is not specific to the library.

2018-03-13T12:12:53.000311Z

Ah, that makes sense… Like getting really good at Guitar Hero instead of learning guitar.

sundarj 2018-03-13T12:13:27.000142Z

sort of; those functions are part of Clojure's 'standard library': i.e. a library of functions that comes with the language

daveliepmann 2018-03-13T12:13:32.000176Z

or being good at doing your taxes using TurboTax

2018-03-13T12:13:49.000223Z

Let’s not talk about taxes… 😓

sundarj 2018-03-13T12:13:56.000393Z

generally when people say 'library' they mean one external to the language, third-party or otherwise

sundarj 2018-03-13T12:14:10.000354Z

so for example, a library for working with dates and times in Clojure is https://github.com/dm3/clojure.java-time

2018-03-13T12:15:18.000454Z

(I have to submit corporate taxes for the first time this year, and it seems very complicated and expensive.)

😱 1
sundarj 2018-03-13T12:15:32.000330Z

your actual problem might be, say, making a calendar app - that library just helps you with the date/time bits specifically

2018-03-13T12:16:02.000079Z

Ah, got it. Thank you!

sundarj 2018-03-13T12:17:54.000108Z

no problem 🙂

2018-03-13T14:38:28.000196Z

I’ve tried googling, but I don’t quite understand what is meant by “lazy behaviour” - can anyone explain it in clearer terms?

2018-03-13T14:45:40.000895Z

The basic idea is “deferred execution”. For example, (range) with no arguments will return a theoretically infinite series of integers, starting with 0. It should lock up your computer while it dumps an endless series of digits, but in practice you can do (take 10 (range)) because range is lazy. It only returns values as you ask for them, and then waits for you to ask for more.

2018-03-13T14:46:38.000903Z

Of course, if you type (range) at the REPL, you are asking for an infinite series of integers (and if you ever try it, you’ll remember next time not to do that, guaranteed!)

2018-03-13T14:47:44.000111Z

What would the alternative be? What’s the opposite of lazy?

2018-03-13T14:48:01.000330Z

Usually people use “eager” to refer to the opposite of lazy

sundarj 2018-03-13T14:52:06.000611Z

map is lazy: you can do (take 5 (map inc (range))) and only those 5 calls to inc will be done mapv however is eager since it returns a vector (so all the elements need to be present at once): if you do (mapv inc (range)) your program will indefinitely inc the elements of the infinite sequence until you cancel it

sundarj 2018-03-13T14:53:28.000338Z

boot.user=> (take 5 (map inc (range)))
(1 2 3 4 5)
boot.user=> (mapv inc (take 5 (range)))
[1 2 3 4 5]
boot.user=> (mapv inc (range))

(forever)

mfikes 2018-03-13T14:56:30.000060Z

And riffing on Sundar's first example, it is a lazy sequence that is only realized by virtue of it being printed by the REPL.

cljs.user=> (def xs (take 5 (map inc (range))))
#'cljs.user/xs
cljs.user=> (realized? xs)
false
cljs.user=> (first xs)
1
cljs.user=> (realized? xs)
true
cljs.user=> (realized? (rest xs))
false

1
2018-03-13T14:59:15.000534Z

What does xs mean in that context?

sundarj 2018-03-13T14:59:31.000420Z

the plural of x: a sequence of x's. it just means a generic sequence you haven't given a specific name

sundarj 2018-03-13T15:00:13.000932Z

here's another example that makes it more visible what laziness does:

boot.user=> (type (map println [1 2 3]))
clojure.lang.LazySeq
boot.user=> (type (mapv println [1 2 3]))
1
2
3
clojure.lang.PersistentVector

2018-03-13T15:09:06.000014Z

Sorry, I still don’t quite understand. Maybe I should come back to this…

sundarj 2018-03-13T15:24:24.000899Z

fair enough 🙂 perhaps thinking about it like functions will help. (fn [] (+ 1 2)) creates a function that evaluates (+ 1 2) only when the function is called (the result of the function is asked for). lazy sequences similar to that, except the deferral applies to multiple values instead of just one: (map inc (range)) creates a lazy sequence that, when asked for a number of items, will call inc on each of those items as it returns them

sundarj 2018-03-13T15:25:26.000284Z

(map inc (range)) by itself will only create a lazy sequence, nothing else. it's only when the items inside it are asked for that any code is run (in this case (inc 0) (inc 1) etc)

sundarj 2018-03-13T15:26:12.000309Z

if you run that in a repl, the repl will try to print it, and since it needs the items to do that, it will ask the lazy sequence for those items

sundarj 2018-03-13T15:26:33.000551Z

thereby running the code

sundarj 2018-03-13T15:27:39.000097Z

it's 'lazy' because it only does the work when you force it to do so by asking for the items

sundarj 2018-03-13T15:28:16.000180Z

if you do not ask for any items, it will not do any work

orestis 2018-03-13T16:21:14.000885Z

I’d say lazy vs eager evaluation is a bit advanced for this channel, and for someone’s first steps into programming it could be safely ignored.

2
orestis 2018-03-13T16:26:50.000821Z

But I also can’t resist giving my own take on it 🙂 — when running a program, usually the computer will take expressions one by one and “run” them (or evaluate them). For example when encountering (+ 1 2) the computer will run and replace that with 3. This is the usual case and it’s called “eager evaluation”. By contrast, in “lazy evaluation”, the computer will instead “remember” (+ 1 2), but not actually run the computation immediately. It will instead wait until some other part of the program needs the value of (+ 1 2) right now because it can’t progress without it. Only the computer will actually make the addition and calculate the result.

orestis 2018-03-13T16:27:27.000931Z

This is an oversimplified overview of lazy/eager. Some languages like Haskell are fully lazy. Clojure is not, but has lazy sequences.

orestis 2018-03-13T16:28:50.000379Z

In Clojure, a lot of things return a lazy sequence. This is a sequence that is “remembered” by the computer, but not actually produced yet.

orestis 2018-03-13T16:30:06.000849Z

For example, (range 100000000000000000) — this should generate a gajillion of numbers and very quickly grind the computer to a halt. However (take 3 (range 100000000000000000)) executes immediately — what gives? The range is lazy, and since we need only the first 3 elements, the rest are never produced.

orestis 2018-03-13T16:31:31.000054Z

There is a catch like most people have said, that typing (range 100000000000000000) in a REPL will usually result in the REPL trying to print all of those numbers, and of course taking up all your memory and CPU.