Fork me on GitHub
#beginners
<
2020-08-17
>
Ty00:08:59

The difference between -> and ->> is just where it inserts the argument, right?

dpsutton00:08:44

Correct. “Thread first” versus “thread last”

Ty00:08:18

What's with the # here?

(filter #(or (zero? (mod % 3)) (zero? (mod % 5))))

dpsutton00:08:31

Check out the “dispatch” section

Ty00:08:29

Ah thank you

Ty00:08:07

So basically this is an anonymous function and % represents the first argument passed to it

Igor Lenterman02:08:57

how should I start learning clojure?

dehli02:08:36

When I started learning I first read through: https://www.braveclojure.com/foreword/ It does encourage emacs usage so if you’re not interested in using emacs you could skip that section!

Ty02:08:16

Ah that's true. I read through that a few months before I actually started hacking away on code

Igor Lenterman03:08:06

thank you! what do you think of cursive vs emacs for a clojure IDE?

seancorfield03:08:02

Use whatever you already know, if it has a Clojure integration.

seancorfield03:08:20

You don't want to be learning a whole new editor as well as a whole new language.

Igor Lenterman03:08:49

I’m familiar with JetBrains as I have used PyCharm extensively, so perhaps Cursive makes more sense for that reason

seancorfield03:08:28

If you are familiar with IntelliJ, Cursive will be your best bet.

practicalli11:08:46

@igor.lenterman these videos and books should help you (all free) https://practicalli.github.io/

practicalli11:08:53

To learn Clojure core functions, I found the best approach for me was the 4Clojure challenges, far more specific to learning clojure than anything else

practicalli11:08:39

https://exercism.io/ seems to be far better than any of the general challenges websites. Codewars is very close to working as a developer, in that the descriptions are dire and much of the information is hidden from you.

Ty02:08:31

I've found that the only way is to dive right in and start making mistakes

Ty02:08:58

If you're used to the web and javascript there are a bunch of good clojurescript tutorials out there that you can follow

Ty02:08:28

There's also code katas and exercises. Check out codewars. It's a website that lets you do little exercises to try and level up

Ty02:08:00

More than anything make sure your editor/environment are setup to help you succeed

Igor Lenterman03:08:26

would you recommend any in particular?

Ty02:08:22

What I like about codewars is that you get to see everyone else's solution once you've finished a problem successfully. So you can learn tricks and new things from more experienced clojure folks

practicalli11:08:32

Create a free account on http://www.4clojure.com/ and select Top Users, then follow them to see their solutions (once you have solved a challenge) I also did the first 60+ solutions as videos https://www.youtube.com/playlist?list=PLpr9V-R8ZxiDB_KGrbliCsCUrmcBvdW16

seancorfield03:08:28

Different people learn different ways @igor.lenterman There are some great books for beginners, there are some video courses (some free, most paid), there are also some online tutorials.

Igor Lenterman03:08:20

I think I want to jump right in building projects in Clojure - are there any online resources particularly good for this kind of pratice?

Ty03:08:57

Look at some of the leiningen templates

Ty03:08:17

If you really want to get started right away building real stuff, look for a good figwheel tutorial

seancorfield03:08:36

Depending on your background, you might find Clojure pretty frustrating if you try that approach...

Igor Lenterman03:08:00

My background is in Javascript and Python, primarily BE development work

Ty03:08:01

I guess that is only if you want to learn clojurescript and build web stuff. I also agree with Sean. I tried what you are describing and I had to take a big step back

seancorfield03:08:06

Clojure is very different to most other languages.

seancorfield03:08:34

If you're used to backend development, stick to Clojure to start with.

Igor Lenterman03:08:37

what’s the difference between Clojure/Clojurescript/Fighweel? What’s the best place to start?

seancorfield03:08:00

Figwheel is a build tool for ClojureScript. Ignore it for now.

seancorfield03:08:33

Take a look at https://github.com/seancorfield/usermanager-example/ -- that's a "simple" Clojure web app that uses Jetty, has a database, and renders HTML from Django-like templates.

Igor Lenterman03:08:23

im so excited to learn

seancorfield03:08:19

I think the Clojure CLI / deps.edn is easier to learn than Leiningen but a lot of the tutorials still use lein because it's been around for a decade, whereas the CLI is a fairly new release -- but it's from the official Clojure core team.

Igor Lenterman03:08:37

what are each of those? • CLI • Deps.edn • Lein Why would I use one over the other?

seancorfield03:08:57

Are you using macOS / Linux or are you using Windows?

seancorfield03:08:24

Do you use brew?

seancorfield03:08:21

That shows how to install/use the Clojure command-line and deps.edn (which describes your project's dependencies). Like project.json in JS / npm projects? (I don't know, I have never used JS)

Igor Lenterman03:08:58

ahhh I see! yes deps.edn maps to package.json

Igor Lenterman03:08:42

so would you recommend learning from the CLI vs coding in the cursive IDE?

seancorfield03:08:05

You'll need to learn about deps.edn in both cases.

seancorfield03:08:20

I find the CLI far more useful -- but then I don't like IDEs.

seancorfield03:08:36

(in particular I really don't like IntelliJ! 🙂 )

seancorfield03:08:24

I used Emacs for about 20 years, then switched to Atom. So now I use Chlorine for the Clojure integration with Atom.

Igor Lenterman03:08:37

I’m going to try from the command line! thank you for all your help

seancorfield04:08:42

I use the CLI a lot. I start my REPL that way and it's how I run tests and build deployment artifacts ("uberjars").

seancorfield04:08:07

And you can start a REPL from the CLI that you can then connect to from your IDE/editor.

seancorfield04:08:42

It's good practice to do everything from your editor, in terms of writing and evaluating code -- don't just type stuff into the REPL.

seancorfield04:08:36

You really want to make sure you have a very tight REPL-based workflow to get the best out of Clojure.

seancorfield04:08:01

Check out Stu Halloway's talks on "REPL-Driven Development" and "Running With Scissors".

seancorfield04:08:16

If you're willing to spend money, $49 buys you a month's access to all of http://PurelyFunctional.tv and the REPL-Driven Development course is awesome.

seancorfield04:08:45

(I have a monthly subscription)

practicalli11:08:19

I am working on this free book (and video series) on WebApp / Api development with Clojure over the next few months https://practicalli.github.io/clojure-webapps/

Igor Lenterman16:08:11

wow thanks guys!

Ty03:08:08

How can I make this less redundant when it comes to the casing of the characters in the set that I've defined?

Ty03:08:13

(ns disemvowel-trolls)

(def vowels #{ \a \e \i \o \u \A \E \I \O \U })

(defn disemvowel
  [string]
  (apply str (filter (complement vowels) (seq string))))

seancorfield03:08:13

(let [vowels "aeiou"] (into (set vowels) (str/upper-case vowels))) perhaps?

seancorfield03:08:24

(where str is clojure.string)

seancorfield03:08:11

Also, @tylertracey09 you don't need seq in there: filter automatically calls seq for you.

seancorfield04:08:45

@tylertracey09 Also, you can do (remove vowels string) rather than (filter (complement vowels) string):

user=> (require '[clojure.string :as str])
nil
user=> (def vowels (let [vowels "aeiou"] (into (set vowels) (str/upper-case vowels))))
#'user/vowels
user=> vowels
#{\A \a \E \e \I \i \O \o \U \u}
user=> (defn disemvowel [string] (str/join (remove vowels string)))
#'user/disemvowel
user=> (disemvowel "Hello, World!")
"Hll, Wrld!"
user=>

Ty04:08:01

Oh wow that's neat

tomd08:08:14

You could also look at the regex case-insensitivity flag with clojure.string's replace fn:

(defn disemvowel [s] (str/replace s #"(?i)[aeiou]" ""))

zackteo06:08:15

Hi im stuck getting ({\A 20} {\T 21} {\C 12} {\G 17}) when I need this for output {\A 20, \T 21, \G 17, \C 12}

(defn nucleotide-counts [strand]
  (let [types '(\A \T \C \G)
        m     {}]
    (for [type types]
      (assoc m type (count-of-nucleotide-in-strand type strand))
      )
    ))
Any advice for how to tackle such problems? 😮

Ty06:08:54

I think just switch the C and G in your types

Ty06:08:17

Oh nvm I see

zackteo06:08:29

No no i issue yeah is the sets inside the list

Ty06:08:43

Right I see. I'm thinking. I'm a beginner as well

zackteo06:08:22

ahhhh no worries 🙂 im sure is to do with how for works. But don't know how to approach such things

zackteo07:08:00

Okay I can do this ... but needless to say, there's something very wrong - guess ill check the solutions hmmm

(defn nucleotide-counts [strand]
  (let [types '(\A \T \C \G)
        m     {}]
    (into (hash-map) (for [type types]
                       (assoc m type (count-of-nucleotide-in-strand type strand))
                       ))
    ))

zackteo07:08:12

@tylertracey09 apparently can be as simple as

(defn nucleotide-counts [strand]
  (merge {\A 0 \T 0 \C 0 \G 0}
         (frequencies strand))
  )

mloughlin08:08:49

I think it's normal to experience the feeling of spending ages on a problem and then realising clojure.core already has the fn :D

mloughlin08:08:36

The nice thing about it is you can call (clojure.repl/source frequencies) and see the implementation.

practicalli12:08:33

Yes, looking at the source of clojure.core can be very enlightening. I learned to do this when solving http://4clojure.com challenges, especially those that exclude the use of certain functions.

Ty17:08:18

Thats dope

practicalli12:08:50

With a string I can use re-seq and a regex #"[a-z]" to create a sequence that only contain characters a to z. Is there something similar for a sequence of characters? The aim is to filter a string to ensure it only contains alphabetical characters and sort the characters in the string in alphabetical order

(defn alphatize
  [string]
  (set (re-seq #"[a-z]" string)))

(defn longest [string1 string2]
  (string/join
    (apply sorted-set
           (clojure.set/union (alphatize string1)
                              (alphatize string2)))))
Any alternatives ?

mloughlin13:08:37

🤔 replace the re-seq with a hard-coded (def alphabet (set \a \b ... \z) and a filter transducer?

mloughlin13:08:49

(into (sorted-set) (filter #{\a \b}) "cba") => #{\a \b}

noisesmith13:08:54

it would be interesting to benchmark and compare with string <-> set conversions plus regex, it seems to be about equal in terms of abstraction

mloughlin13:08:35

doesn't seem like there's much in it, with the provision that I can't figure out how to convert the single-length strings to chars without making it really slow (for the regex solution) (test input generated by me smashing my keyboard for 30 seconds...)

"transducer:"
"Elapsed time: 0.2548 msecs"
#{\d \e \f \g \h \i \j \k \l \o \p \r \t \u \y}

"regex:"
"Elapsed time: 0.2376 msecs"
#{"d" "f" "e" "p" "j" "t" "i" "k" "r" "y" "g" "l" "u" "h" "o"}

hoopes23:08:30

sorry for repeating the question, but if there were something obvious to do to get tools.namespace refresh or refresh-all working from within a docker container with lein, i'd be much obliged. I added a SO question here: https://stackoverflow.com/q/63443115/482425 . Is there some special file/directory to mount for that to work? Thanks!

chucklehead01:08:42

What host/container OS for Docker? Do you have an example of a docker-compose.yml or overall project structure you could share?

seancorfield01:08:39

@U0G2E3SEM Just to check: all of your source files are accessible inside that Docker image, at the same paths as in your editor?

hoopes01:08:28

well... they're not at the same path as in the editor, but i'm doing refresh from the lein repl shell inside the container

hoopes01:08:51

it's using the clojure:openjdk-14 image

seancorfield01:08:59

Personally, I avoid all the reload/refresh stuff -- it seems so problematic for so many people. I just eval every single change from my editor, as I make it -- I often don't even save changes, I just eval them. That way the REPL is always up-to-date.

hoopes01:08:20

service:                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                             
        image: clojure:openjdk-14                                                                                                                                                                                                            
                                                                                                                                                                                                                                             
        working_dir: /app                                                                                                                                                                                                                    
                                                                                                                                                                                                                                             
        stdin_open: true                                                                                                                                                                                                                     
        tty: true                                                                                                                                                                                                                            
        command: bash                                                                                                                                                                                                                        
                                                                                                                                                                                                                                             
        depends_on:                                                                                                                                                                                                                          
          - db                                                                                                                                                                                                                               
                                                                                                                                                                                                                                             
        ports:                                                                                                                                                                                                                               
          - 8000:8000                                                                                                                                                                                                                        
                                                                                                                                                                                                                                             
        volumes:                                                                                                                                                                                                                             
                                                                                                                                                                                                                                             
          # Put the clojure app in /app                                                                                                                                                                                                      
          - ./my-clj-src:/app                                                                                                                                                                                                                

hoopes01:08:20

Relevant section from docker-compose.yml

hoopes01:08:08

i also don't have a good way to have my editor on the host machine hooked up to the running repl, because the repl port changes, and i don't have a whole port range forwarded out of the container 🙂

hoopes01:08:17

if there's an obvious workaround for that too, i'm all ears

seancorfield01:08:59

I don't use Leiningen and I don't use nREPL so I probably don't have any good suggestions for your current setup.

hoopes01:08:39

when i run the repl on the host machine, refresh works, but i don't have my entire environment vars/other containers/etc etc etc

seancorfield01:08:15

I use a Socket REPL so I control the port number 🙂 But most editors require nREPL instead 😞

seancorfield01:08:01

I have my DB (MySQL), Redis, and ElasticSearch all Dockerized and I start all of those with docker-compose up. But I run my REPL on the host machine (to avoid exactly this sort of complexity).

hoopes01:08:06

yeah, i have much to learn about the whole editor/repl jedi moves

hoopes01:08:10

you still deploy your app dockerized though?

seancorfield01:08:22

We do not use Docker in production, no.

seancorfield01:08:21

We build uberjars for deployment and run those on servers with just a JDK in a data center.

seancorfield01:08:58

We can run the same uberjar locally, but for active development, we just start a REPL with all our deps and work live against that. We have a monorepo with 35 subprojects (each has a deps.edn file) and we build 12 applications (`.jar` files) from that.

hoopes01:08:34

i currently have fear of not running in a container, and having to deal with eg ec2 images or whatever. i guess i'm just most comfortable containerized these days. thanks for giving me a shout, if i ever figure this out, i'll ping back in the channel.

seancorfield01:08:54

I would definitely suggest trying to align file system paths between your editor and your container so that you can work effectively from inside your editor against a REPL that "just happens" to live in a container, but otherwise has the same view of the world (filesystem-wise).

seancorfield01:08:29

That way, all the usual stuff that works in the editor with a local REPL will also work with a containerized REPL.

chucklehead01:08:50

Sorry if I missed it, but what host OS are you developing on? I don’t know if it’s still an issue but as of a year or so ago docker for Windows didn’t propagate filesystem change notifications to running containers.

hoopes01:08:31

@U015879P2F8 i'm on ubuntu 19.10

seancorfield01:08:05

Linux host O/S and a Linux container?

hoopes01:08:23

yeah, the container is debian

noisesmith02:08:05

try checking what the clojure process inside docker would want to load, when refreshing - if the refresh doesn't error, it's reading the ns from somewhere right?

org.noisesmith.hammurabi.hold-my-beer=> (io/resource "org/noisesmith/hammurabi.clj")
#object[java.net.URL 0x7ac802ae "file:/Users/justin.smith/hammurabi/src/org/noisesmith/hammurabi.clj"]
between the docker config and project.clj classpath setup, there are multiple combination of fs mounting / container access that let your editor and the clojure inside the container agree on where the source code you are editing is located, io/resource is an easy way to see what clojure is actually using

noisesmith02:08:53

it would be nice if there was a core function where you give it a namespace, and clojure tells you what file or resource it would load to get it - but the ns/path translation is trivial so it's not super onerous

jumar06:08:06

[this might be an obsolete info] there may be some issues with inotify package on some linux distributions (notably Alpine Linux). Also editors (which I assume you're running on the host) commonly create a new inode for the modified file and just replace the old one - you may have some issues with file system permissions -> Nick Janetakis recommend this in his Docker course when running docker on Linux: --user "$(id -u):$(id -g)"

jumar06:08:54

Moreover, you may use lein repl :start :port ... to start it on a fixed port: https://stackoverflow.com/a/52482092/1184752

noisesmith07:08:33

oh - I'd thought from the initial question that refresh was being called explicitly, rather than relying on a watcher

noisesmith07:08:29

it explicitly mentions "calling refresh"

jumar10:08:52

So it looks like the last modification timestamp isn't reflected for some reason. Should be easy to verify with clojure.tools.namespace.dir/scan-dirs (and possibly other lower-level functions)

hoopes13:08:45

thank you @U06BE1L6T and @U051SS2EU - i will try both of these this evening when i get a chance!

hoopes02:08:32

fwiw, if i mount my code at the same path name in the container as the host, no difference. I've been able to use conjure in vim to reload changed files though, so i'm kind of sneaking in the back door, in a sense. kinda strange that i need my editor connected to a running repl to properly reload code, but whatevs, i'm in the game at least

noisesmith05:08:56

presumably because the editor knows which file changed via its own state, rather than relying on the file system?