Fork me on GitHub
#beginners
<
2022-02-11
>
seancorfield00:02:22

@ray.gavin97 immutability really helps eliminate a whole class of bugs, but we try to write small, pure functions and evaluate them as we write them so the workflow is pretty different: we always have a REPL running and our editor connected to it, and we write example code inside (comment ...) forms in our source files where we can try out small pieces of code -- "REPL-Driven Development".

seancorfield00:02:31

I've worked in a wide variety of languages over the decades -- some typed, some untyped -- and I do not miss type-checking at all. In fact, I find I'm a lot more productive in Clojure (without types) than pretty much any language I've used before.

💯 2
👍 2
john2x00:02:09

If I make a change to spec, which is already loaded in a REPL, do I need to restart the REPL session for the changes to take effect? How do I inspect a spec's definition?

seancorfield00:02:07

You should just be able to re-eval the s/def form. You can get a Spec's form using get-spec and form, as I recall. See https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html

seancorfield00:02:29

(you pretty much never need to restart a REPL -- unless you're updating a specific library dependency that you already had loaded and you want to ensure your REPL is using that new version (you can add new library dependencies to a running REPL but that isn't very commonly done and depends on the tools you are using to start your REPL)

john2x00:02:37

awesome, thank you! yes, looks like re-evaling the spec updated it in the REPL

1
mbjarland06:02:17

@ray.gavin97 What @seancorfield said. Would just like to add that this is something tangible that is not easy to convey with words, it has to be experienced. The "flow" you get into with repl driven development is fantastic, but it takes a bit of time and effort to get there.

Nedeljko Radovanovic07:02:59

Hello people 👋 Is someone familiar with this error? Syntax error ( AssertionError ) compiling at (……). Assert failed: ( every? :id % ) I am making a terminal app and using cli-matic as argument parser library, every function works in repl but when i try to call it from terminal i get this error

Ferdinand Beyer16:02:25

Hey @U02US0AS16K Hard to say without more context. Look at the exception (compiling at <where?>) to see what code is running. My guess would be that you are using a macro that expects :id to be present in maps, but it’s not.

Nedeljko Radovanovic16:02:57

Fixed the problem 😊

🙌 1
Nedeljko Radovanovic07:02:00

I created configuration file and i am calling from main function like this (run-cmd args configuration)

hiredman07:02:40

There is an a form like (assert (every? :id %)) and the assert is failing

hiredman07:02:00

The rest of the stack trace should give you file and line information

Nedeljko Radovanovic08:02:57

It gives me line where it was called

lassemaatta09:02:12

suppose a map contains a thunk (`{:foo (fn [] ....)}`) and you want to realize it in-place; is there a more concise way than (update m :foo apply [])?

emilaasa10:02:33

I don't think so, but you could make a function that does that

Alex Miller (Clojure team)13:02:05

Is there a reason you're not using a multimethod dispatching on keywords? If so, the calling pattern would be just invoking the multimethod.

lassemaatta14:02:49

the context here is that I have an existing function (which I'm reluctant to change) which produces such a map

lassemaatta14:02:59

and what I want to end up with is something like {:bar 1 :zed "asd" :foo "some result"}. I could always define a (defn realize [x] (x)) and use it with update, but I was wondering if there's some clojure.core function which already does this.

Alex Miller (Clojure team)14:02:33

don't think there's anything better than where you started then

👍 1
noisesmith17:02:28

@U0178V2SLAY one thing to consider is using a delay instead of a function, then using force

noisesmith17:02:03

delay uses a thunk internally, but it's specifically designed for this sort of use case

noisesmith17:02:27

and force is handy because it's identity when called on a value that isn't a delay

lassemaatta17:02:54

not a bad idea 👍

GGfpc12:02:54

Hello, I'm trying to write the following fspec. It works, but is there a more concise way of writing that? Three lines of specs for a single parameter seems like a lot

(s/def ::datasource int?)
(s/def ::db (s/keys :req-un [::datasource]))
(s/def ::opts-db (s/keys :req-un [::db]))

(defn receives-db
  [{:keys [db]}]
  db)

(s/fdef receives-db :args (s/cat :arg1 ::opts-db))

; Example
(receives-db {:db {:datasource 1}})
=> {:datasource 1}

Alex Miller (Clojure team)13:02:05

I don't think there's any better way than that

James Amberger16:02:16

I have just come across the notion of namespaced keywords.

James Amberger16:02:11

@seancorfield I see you have had some fun discussing these online!

1
James Amberger17:02:13

But, I don’t really get it, and I don’t see much of an intro to them. My understanding of keywords is that they’re used as map keys and as functions into the map

James Amberger17:02:40

but the intro guide sections on hashed collections and on namespaces don’t mention namespaced keywords at all

James Amberger17:02:54

what, if anything, is the hello-world of these things?

James Amberger17:02:30

is :hello/world special to Clojure or is the slash separation thing only a lexical convention?

Alex Miller (Clojure team)17:02:50

it's special - those are separate namespace and name parts

Alex Miller (Clojure team)17:02:56

the namespace gives you an opportunity to disambiguate short names, just like namespaces let you disambiguate function vars

James Amberger17:02:05

and it’s not sufficient that you would be calling one or another map

Alex Miller (Clojure team)17:02:13

if you want to attach semantics to a keyword, you should namespace to a place that is something you "own"

Alex Miller (Clojure team)17:02:00

if your app A does this, and lib B does it, and lib C does it, you can have attributes from A, B, and C in the same map and not collide

Alex Miller (Clojure team)17:02:31

not everything has this need for disambiguity, so unnamespaced keys are fine too in that case

James Amberger17:02:34

ok, so e.g. if I were doing something with a relational database

James Amberger17:02:03

I could (merge employee office) and if those things had namespaceed keys, I could end up with {:employee/id :office/id …}

seancorfield19:02:46

With next.jdbc, if you JOIN across employee and office tables, you will get a hash map with keys from employee qualified with that and keys from office qualified with that. An example from a database I have handy:

dev=> (jdbc/execute-one! db-spec ["select u.id, u.username, s.* from user u join status s on u.statusid = s.id where u.id = ?" 9])
{:user/id 9, :user/username "derekpope", :status/id 1, :status/name "approved"}

👍 1
James Amberger17:02:07

Is there some use of keywords other than as map keys that is relevant here?

Ben Sless18:02:27

Generally, it gives you the ability to refer to resources and identities in a very unambiguous way. You can model your domain that way, and given that the "root" namespace is known, everyone can know what you're referring to if you send them a fully qualified name

Ferdinand Beyer17:02:32

You will encounter keywords in many places in Clojure. Some functions use them to provide a sort of mini-language. E.g. the require function loads other namespaces, and it supports (require :reload '[some.namespace]) to reload namespaces, (require '[clojure.string :as str]) to provide an alias etc. Notice how :reload and :as are keywords.

JohnJ17:02:43

you can use them anywhere you need to name things

James Amberger17:02:05

Much clearer now—thanks all

Alex Miller (Clojure team)17:02:38

think of keywords as enumerated values - like constant strings (but better perf, and can be used as functions for self-lookup in a map)

Stan Foley18:02:20

Hi. I am trying to get https://github.com/unclebob/Euler working using vscode and Calvo and I get a compiler error in the test files for "unresolved symbol". Can I get some help with it please?

noisesmith18:02:37

the thing we need to see is hidden by a popup - the ns form

noisesmith18:02:06

the describe macro is not part of clojure,core, so it will need to be brought in via that ns form, which is where your error is likely to be

Stan Foley18:02:46

Hi. Ok I just took another capture

noisesmith18:02:57

also, describe is part of a testing framework that I'd not suggest using

Stan Foley18:02:17

(ns e1.core-spec (:require [speclj.core :refer :all] [e1.core :refer :all]))

Stan Foley18:02:55

within the same core_spec.clj , I need to add a require ?

noisesmith18:02:30

that ns form should work, so I don't know why you were getting the error. your linter doesn't like :refer :all (which is suggested by speclj but considered bad style)

noisesmith18:02:05

it could be that clj-kondo is so opinionated about not liking :refer :all that it refuses to load it? might be a good question for #clj-kondo. but with an early project I think it's simpler to just use clojure.test instead of speclj

Stan Foley18:02:50

I changed to clojure.test but nothing changed

noisesmith18:02:28

with clojure.test, you would use deftest and is instead of describe and should=

(ns something.foo-test
  (:require [clojure.test :refer [deftest is]))

(deftest some-function-test
  (is (= (f 9) 3))

Stan Foley18:02:09

ok let me try

noisesmith18:02:47

also, if you really prefer speclj I think that clj-kondo would be less likely to complain if you used refer like above, enumerating the individual functions to refer

Stan Foley18:02:03

how do I address the error for "it" please?

Stan Foley18:02:32

let me get another capture

Stan Foley18:02:36

deftest seems to work

noisesmith18:02:03

(testing "some testing description" (is ...)) - I think testing does what it does in the other lib

Stan Foley18:02:54

I guess I can just add "it" inside the brackets at the top

noisesmith18:02:01

also - is is tricky, it doesnt' compare its args, it just checks if the first one is nil or false - so thos is calls need = inside

noisesmith18:02:28

that doesnt' work because it is not something clojure.test defines, but testing is

Stan Foley18:02:26

oh when I add the equal sign I get the error again

noisesmith18:02:45

it should look like (is (= 0 (sum-multiples-3-5 0))) - = is a function you call inside is

noisesmith18:02:33

or the shorthand (is (zero? (sum-multiples-3-5 0)))

Stan Foley18:02:22

ahh ok. I'll change them . Do you know where is the documentation for deftest please?

noisesmith18:02:37

you can get it directly in a repl with doc

(cmd)user=> (require 'clojure.test)
nil
(cmd)user=> (doc clojure.test/deftest)
-------------------------
clojure.test/deftest
([name & body])
Macro
  Defines a test function with no arguments.  Test functions may call
  other tests, so tests may be composed.  If you compose tests, you
  should also define a function named test-ns-hook; run-tests will
  call test-ns-hook instead of testing all vars.

  Note: Actually, the test body goes in the :test metadata on the var,
  and the real function (the value of the var) calls test-var on
  itself.

  When *load-tests* is false, deftest is ignored.
nil
there are also sites like clojuredocs https://clojuredocs.org/

Stan Foley18:02:19

Ah. well thank you so much. I'll make the changes

pez10:02:07

@U032K6B0EEA regarding docs for deftest. You can hover it and see the docs, including information from http://clojuredocs.org. And you can cmd/ctrl+click to open up the source where deftest is defined. You are super welcome to join the #calva channel. (You might become the 1000th member there even 😀).

Drew Verlee03:02:01

that is quite the color choice stan

🙃 1
seancorfield19:02:46

With next.jdbc, if you JOIN across employee and office tables, you will get a hash map with keys from employee qualified with that and keys from office qualified with that. An example from a database I have handy:

dev=> (jdbc/execute-one! db-spec ["select u.id, u.username, s.* from user u join status s on u.statusid = s.id where u.id = ?" 9])
{:user/id 9, :user/username "derekpope", :status/id 1, :status/name "approved"}

👍 1