Fork me on GitHub
#beginners
<
2020-04-25
>
joshkh10:04:18

how can i make a string of an expression bound in my namespace, including indentation and whitespace? i've taken a look at clojure.repl/source which is close to what i need, but it my case i want to inject the expression into a hiccup template (as a string) at the time of evaluation.

andy.fingerhut13:04:59

I wouldn't say such a thing is impossible, but at least the Clojure reader does not preserve this information. I think it will preserve the starting line and column in the input file of some selected expressions, but not all.

andy.fingerhut13:04:33

There are other libraries that can read Clojure code and/or EDN data and preserve other things that the Clojure reader does not.

joshkh17:04:23

hmm. it looks like i can leverage pprint for formatting. in which case, is it possible to read a def'ed expression as a string?

(defn my-func [] 1)

(fn->str my-func)
=> "(defn my-func [] 1)" 

Chris K10:04:08

any thoughts of learning clojure/functional programming before CS in uni? I am a highschool student and spend most of my programming time learning functional programming and clojure. I kinda fear that learning clojure and functional programming might mess me up for CS in uni, because I believe they are more based on lower level and imperative langauges. Any thoughts on this? (I hope this is a valid question for the beginners section)

joshkh12:04:35

i'm sure a lot has changed since i was at university, but one common theme of nearly all the languages i remember using was that they were strongly typed, which of course we don't see in Clojure. in my opinion it's worth learning any strongly typed language at a basic level just because they're unavoidable in the real world, and especially at uni. but focusing on Clojure certainly will not "mess you up" for a CS degree, and in fact you might find yourself writing better object oriented code with reduced state because of your exposure to Clojure / LISP.

Chris K13:04:47

oh I see so basically what you are saying is that by learning functional lisp programming, I would actually be able to write better OOp code

sova-soars-the-sora13:04:22

I think it's a great idea to learn Clojure before a CS program. as @joshkh said, it'll improve your understanding of coding principles. Lower level stuff you can pick up easily. Basically, you need to name every drawer where you keep something, and different things can manipulate the drawers without other things knowing. This is unsatisfactory for sanity. However, it is the decades-long norm in CS. I think it's easy to pick up this habit later, and I don't know why you would want to 😄 If you are doing low-level device programming, sometimes you have not the clock cycles to spare, but Church-Turing should give you confidence that anything you can do with a lisp you can do with imperative and vice versa, it's just taking into account psychic peace that draws a clear victory line for me.

Chris K13:04:02

wow this answer is great thxs

joshkh17:04:37

knowing Clojure might also make you the hippest kid in your class 😎

😎 4
Bill Phillips17:04:13

Chris, my first serious programming instruction was done in Scheme in college, and it was wonderful. It expanded my mind and exposed me to new things, and the following semester I was writing linux networking code in C! I also have a friend who learned programming with Lisp in high school, and it was a formative experience for him, too. You are learning cool things and they’re fun! There’s NO downside to that AT ALL. 🙂 I have never ever ever met anyone who ever said “I regret having fun learning a cool unusual programming thing”

Chris K10:04:59

@U010GL90FN0 Thxs for your answer! I already thought that lisp is such a cool language, and now I'll just keep going 😄

Nate Guerin17:04:48

For what it's worth, I help teach courses in a top-ten CS program, and we teach scheme as well. LISPs are nice when saying things about the mathematical properties of computer programs

Chris K00:04:55

Hmm. I heard that a lot. Of how lisp is good for mathematical representation, but I didn't quite get it because, when you write inside parentheses and with prefix notation, it doesn't look like the conventional mathematical notations we usually see. I know notation and the styling isn't rly anything but would it be ok for you explain that a little bit? Thxs @U0120B5AN2W

Nate Guerin00:04:56

Sure. The notation doesn't really matter. It's more because of the properties of referential integrity (https://en.wikipedia.org/wiki/Referential_transparency) and because you can prove things about them using mathematical induction.

Nate Guerin00:04:08

It's hard to prove things about programs where the state varies all of the time.

Chris K11:04:11

Oh I see. So basically immutabilty, pure functions, and the things you talked about mathematical induction and referential transparency makes this better clojure better than other languages with mutable data types

Chris K11:04:24

That's cool I didn't know that Thxs 😄

andy.fingerhut12:04:58

I don’t think it will mess you up. If you want to get a CS degree, you should learn more than one style of programming, even if not all styles are equally used or productive

andy.fingerhut12:04:53

It may make you annoyed at having to do projects in language styles that you do not prefer, but hopefully you can view those as part of the learning process

Timofey Sitnikov12:04:45

Hello all. would like to use https://github.com/weavejester/ragtime with clj and not Leiningen. How can I set it up? Should I write a lib? or can database configs go into the deps.edn?

joshkh12:04:27

are you familiar with deps.edn? if so then you can include ragtime as a dependency and then require it from your code, just as you would with a lein based project.

{:paths ["src/clj" "resources"]
 :deps  {org.clojure/clojure {:mvn/version "1.10.1"}
         ragtime             {:mvn/version "0.8.0"}}}

aisamu12:04:53

Just like with Leiningen! You just have to call ragtime.repl/migrate with your configuration. https://github.com/weavejester/ragtime/wiki/Leiningen-Integration

aisamu12:04:55

> database configs go into the deps.edn You can load the config on the function that's being invoked, just like the Lein integration example. > Should I write a lib? IIUC, no. You may have to write a ns to invoke it with (e.g.) cli -m my.migration

Timofey Sitnikov13:04:13

@U1UQEM078 I am so new to clojure that I am probably tripping on something extremely simple. I am able to apply migrations in REPL:

Timofey Sitnikov13:04:40

Clojure 1.10.1
user=> (ns user (:require [ragtime.jdbc :as jdbc]))
nil
user=> (def config {:datastore (jdbc/sql-database {:connection-uri "jdbc:sqlite:resources/db.sqlite"}) :migrations (jdbc/load-resources "migrations")})
#'user/config
user=> (require '[ragtime.repl :as repl])
nil
user=> (repl/migrate config)
Applying 001-foo
nil
user=>

aisamu13:04:31

(Side note: if you 3 backticks as a code delimiter, the formatting is preserved)

aisamu13:04:43

Yes, that's expected behaviour and essentially what you'll accomplish through the cli invocation

Timofey Sitnikov13:04:19

So my qustion is, to achieve zero argument migrate in REPL, where do I place the def statements so that they are automatically sourced?

aisamu13:04:03

You can put those statements in a file and invoke them with clj filename

aisamu13:04:40

$> echo "(def a 3) (println a)" > example.clj
$> clj example.clj                                                                                                   
3

Timofey Sitnikov13:04:43

Oh, ok that makes sense, so when I am to do migrations I just call that. Thank you, not sure why I did not think of it.😂

aisamu13:04:41

NP! You can also create "task" namespaces and invoke those normally. clj -m namespace will call whatever is in the ns' -main

$> echo "(ns example2) (def a 3) (defn -main [] (println a))" > src/example2.clj
$> clj -m example2                                                                                                   
3

Timofey Sitnikov13:04:35

Hmm, that did not seem to work. It executed the file but did not enter the REPL. There is probably a way to execute a file and enter REPL.

Timofey Sitnikov13:04:21

I added a file migrations.clj at the project root.

(ns user (:require [ragtime.jdbc :as jdbc] [ragtime.repl :as repl]))
   
    (def config
      {:datastore
      ¦(jdbc/sql-database {:connection-uri "jdbc:sqlite:resources/db.sqlite"})
       ¦:migrations (jdbc/load-resources "migrations")})

Timofey Sitnikov14:04:50

Then tried clj -m user and it gave me the following:

Execution error (FileNotFoundException) at clojure.main/main (main.java:40).
Could not locate user__init.class, user.clj or user.cljc on classpath.

Full report at:
/tmp/clojure-5805258766426369068.edn

aisamu14:04:38

The 2 behaviours are expected, let me elaborate

aisamu14:04:42

> It executed the file but did not enter the REPL. That's expected. If you want to be in a REPL, you have to start one (with clj or clj -r)

$> clj 
Clojure 1.10.1
user=> (require 'example2)
nil
user=> (example2/-main)
3
nil
user=>

aisamu14:04:03

> Could not locate user__init.class, user.clj or user.cljc on classpath. That's also expected, since you've added the file at the project root and the default classpath is /src

aisamu14:04:59

Another issue you'll probably run into is that the filename has to match the namespace (due to java constraints). migrations.clj should have (ns migrations)

Timofey Sitnikov14:04:40

Hmm, I want to be able to enter REPL and call migrate and rollback

Timofey Sitnikov14:04:25

OK, so basically, I am trying to enter REPL with the following requirements and definitions applied:

(ns user (:require [ragtime.jdbc :as jdbc] [ragtime.repl :as repl]))
   
    (def config
      {:datastore
      ¦(jdbc/sql-database {:connection-uri "jdbc:sqlite:resources/db.sqlite"})
       ¦:migrations (jdbc/load-resources "migrations")})

aisamu14:04:52

$> clj -e "(require 'example2) (in-ns 'example2)" -r
#object[clojure.lang.Namespace 0x1917d90f "example2"]
example2=> (-main)
3
nil

aisamu14:04:27

Or, using the first script file (without a ns)

$> clj -e '(load-file "example.clj")' -r
3
user=> a
3 

Timofey Sitnikov15:04:16

Hmm, does not seem to keep the configurations:

(ns migration (:require [ragtime.jdbc :as jdbc] [ragtime.repl :as repl]))

(def config
  {:datastore
   (jdbc/sql-database {:connection-uri "jdbc:sqlite:resources/db.sqlite"})
   :migrations (jdbc/load-resources "migrations")})
[I] /home/sporty/clojure/dbt~> clj -e "(require 'migrate) (in-ns 'migrate)" -r
#object[clojure.lang.Namespace 0x51c959a4 "migrate"]
migrate=> (repl/migrate config)
Syntax error compiling at (REPL:1:1).
No such namespace: repl
migrate=>

Timofey Sitnikov15:04:27

For some reason, it does keep the config variable defined, nor are the require statements are honored.

aisamu15:04:32

You have a typo on the ns name. migrate -> migration

🎵 8
joshkh17:04:55

just chiming in 2 hours later, but i also find it useful to create aliases for tasks in deps,edn which you can run without having to specify file names: clj -A:migrate

Timofey Sitnikov17:04:43

@U1UQEM078 wow, that worked, thank you. I am from C++ world, so much new stuff to grasp that I am misspelling :(

👍 4
Timofey Sitnikov18:04:30

@joshkh I have been lookng at the :aliases and tried: :migrate {:main-opt ["-e" \"(require 'migrate) (in-ns 'migrate)\""]}}

Timofey Sitnikov18:04:52

So can C style escape sequences be used?

joshkh18:04:48

hmm, i don't know. i usually just create a namespace for each alias, and then i can leverage arguments from the command line. for example:

{:aliases {:deploy-to-aws {:main-opts  ["-m" "myproject.deploy"]
                           :extra-deps {com.datomic/ion-dev {:mvn/version "0.9.251"}}}}}
and then a namespace with a main function
(ns myproject.deploy)

(defn -main [& [lambda-name]]
  (infof "Pushing lambda to AWS: " lambda-name)
  ...)
and finally call it from the terminal
$ clj -Adeploy-to-aws send-email-lambda
aliases can provide a lot of flexibility when performing tasks programmatically

joshkh18:04:07

and using that :`extra-deps` key let's you isolate dependencies (such as ragtime) that you don't necessarily want in your core code

Timofey Sitnikov19:04:22

@joshkh, that worked!, I just have to figure out how to pass a string in for up and down migrations, but I think I have the information to do it. Just need to put it together. Thank you for helping me through my first day as an aspiring clojerian.

sova-soars-the-sora13:04:22

I think it's a great idea to learn Clojure before a CS program. as @joshkh said, it'll improve your understanding of coding principles. Lower level stuff you can pick up easily. Basically, you need to name every drawer where you keep something, and different things can manipulate the drawers without other things knowing. This is unsatisfactory for sanity. However, it is the decades-long norm in CS. I think it's easy to pick up this habit later, and I don't know why you would want to 😄 If you are doing low-level device programming, sometimes you have not the clock cycles to spare, but Church-Turing should give you confidence that anything you can do with a lisp you can do with imperative and vice versa, it's just taking into account psychic peace that draws a clear victory line for me.

Timofey Sitnikov15:04:16

Hmm, does not seem to keep the configurations:

(ns migration (:require [ragtime.jdbc :as jdbc] [ragtime.repl :as repl]))

(def config
  {:datastore
   (jdbc/sql-database {:connection-uri "jdbc:sqlite:resources/db.sqlite"})
   :migrations (jdbc/load-resources "migrations")})
[I] /home/sporty/clojure/dbt~> clj -e "(require 'migrate) (in-ns 'migrate)" -r
#object[clojure.lang.Namespace 0x51c959a4 "migrate"]
migrate=> (repl/migrate config)
Syntax error compiling at (REPL:1:1).
No such namespace: repl
migrate=>

skykanin16:04:48

Why does calling flatten on a set or map return the empty list unlike when calling it on vectors and lists?

herald16:04:32

flatten only works on sequential collections (as mentioned in the docstring)

👍 4
herald16:04:32

You can have a flatten that works on all collections by substituting sequential? for coll? in the source.

(defn flatten- [x] (filter (complement coll?) (rest (tree-seq coll? seq x))))

Patrick Truong18:04:06

Hello everyone, I’m working on a basic Reagent project using just shadow-cljs. I’m using cider nrepl and VS Code Calva. On my main app file, I have a simple atom for toggle:

(ns alpha-journal.core
  (:require
   [reagent.core :as r]
   [reagent.dom :as dom]))

(def toggled (r/atom false))

(defn app []
  [:div {:class [(when true 'bg-red-500) 'h-screen]}
  ...
which is referenced in the app component. However, when I used my repl and lookup any variables or functions, it gives me the value, but also gives me a warning:
alpha-journal.core=> @toggled
------ WARNING - :undeclared-var -----------------------------------------------
 Resource: :1:2
 Use of undeclared Var alpha-journal.core/toggled
--------------------------------------------------------------------------------
false
What does this warning mean? Why are my variables considered undeclared? How can I get fix this warning? Thanks for all the help 🙂

hindol19:04:22

#reagent channel will be able to help much faster.

👍 4
dpsutton19:04:34

How did you set your repl namespace?

Patrick Truong19:04:37

@U11BV7MTK I don’t think I did explicitly? My shadow-cljs.edn looks like this

{:deps   true
 :builds {:app {:target     :browser
                :output-dir "public/out"
                :asset-path "/out"
                :modules    {:main {:init-fn alpha-journal.core/main}}
                :devtools   {:http-root "public"
                             :http-port 8020}}}}
and my startup log shows shadow-cljs - nREPL server started on port 59071 That is then the port I connected to via Calva

Patrick Truong19:04:27

@U11BV7MTK then in Calva I switched namespace to my main file:

cljs.user=> (ns alpha-journal.core)

dpsutton19:04:41

Your repl prompt shows alpha-journal.core

dpsutton19:04:47

I’m wondering how you set that

dpsutton19:04:03

Ah that’s why

dpsutton19:04:09

You created a new ns

dpsutton19:04:16

Rather than going into the one defined

dpsutton19:04:30

Restart your repl and use require and then in-ns

dpsutton19:04:39

Otherwise you’re defining a new namespace

Patrick Truong19:04:51

Gotcha, thanks I’ll try that out now

Patrick Truong19:04:28

@U11BV7MTK Thanks! that fixed it. Quick question, is there any particular reason why I have to use the delayed eval quote for my namespace: (in-ns 'alpha-journal.core) vs (in-ns alpha-journal.core) ? Thanks for all the help once again

dpsutton19:04:25

It’s not delayed eval. If you eval a random symbol it tries to look it up in the environment and errors if it’s not defined

dpsutton19:04:47

The quote makes sure we are talking about the symbol rather than what that symbol resolves for

dpsutton19:04:15

Think the symbol ‘+ versus the function “add” which it points to in the environment

dpsutton19:04:18

Make sense?

dpsutton19:04:52

So there is no var defined as alpha-journal.core. We’re using the symbol itself

Patrick Truong19:04:15

Got it, thanks for the clarification, helps clear up a lot about the use of quote in general 🙂

dpsutton19:04:09

Definitely!

Timofey Sitnikov21:04:25

Why do I get?

[I] /home/sporty/clojure/dbt~> clj -Amigrate up
Applying 003-zet
Execution error (NullPointerException) at migrate/-main (migrate.clj:10).
null

Full report at:
/tmp/clojure-728581667734213604.edn
When I run the following file with the up argument?
1 (ns migrate (:require [ragtime.jdbc :as jdbc] [ragtime.repl :as repl]))
    2
    3 (def config
    4   {:datastore
    5   ¦(jdbc/sql-database {:connection-uri "jdbc:sqlite:resources/db.sqlite"})
    6   ¦:migrations (jdbc/load-resources "migrations")})
    7
    8 (defn -main [ arg ]
    9   (case arg
   10   ¦ "up" ((repl/migrate config))
   11   ¦ "down" (repl/rollback config)))
Notice that I do not get this issue when the case keyword is not used.

Timofey Sitnikov21:04:02

Oops nevermind, it's hard to let go of C++ and realize that it is all data, below works.

8 (defn -main [ arg ]
    9   ((case arg
   10   ¦ "up" repl/migrate
   11   ¦ "down" repl/rollback) config))

Sergiusz Bleja21:04:32

I'm trying to evaluate someone elses code which uses clojure.core.matrix. Line 3 attempts to set index 100 to 1 but fails. I'm guessing the functionality has changed, but I can't find the way to do it. How can you set the value of a vectorz ?

John MacIsaac23:04:46

I am working my way thru the book, “Clojure for the Brave and True”. And I am stuck on a problem in chapter 4 (https://www.braveclojure.com/core-functions-in-depth/#A_Vampire_Data_Analysis_Program_for_the_FWPD). The problem is to convert a CSV string into a sequence of vectors and then into a sequence of maps. I am stuck in in mapify function.  I dont understand where the  ARGS  for the PARAMS of its second anonymous function come from. The fifth line from the bottom of the code snippet pinpoints the parameters.  

(ns fwpd.core)
;(def filename "suspects.csv")
;(slurp filename)

(def suspects "Edward Cullen,10\nBella Swan,0\nCharlie Swan,0\nJacob Black,
3\nCarlisle Cullen,6\n\n")

(defn parse
  "Convert CSV into a seq of vecs, into rows of columns ([name num] [name num] ...)"
  [string]
  (map #(clojure.string/split % #",")
       (clojure.string/split string #"\n")))

(map #(str->int (second %))
     (slurp filename))

(defn str->int [str] (Integer. str))
(def vamp-keys [:name :glitter-index])
(def conversions {:name identity :glitter-index str->int})
(defn convert [vamp-key value] ((get conversions vamp-key) value))

(defn mapify
  "Convert a seq of vecs into a seq of maps, & convert num-strings to numbers:
  ([\"Edward Cullen\" \"10\"] ...) to ({:name \"Eward Cullen\" :glitter-index 10} ...)"
  [rows]
  (map (fn [unmapped-row]
         (reduce (fn [row-map [vamp-key value]]
                   (assoc row-map vamp-key (convert vamp-key value)))
                 {}
                 (map vector vamp-keys unmapped-row)))
       rows))

(mapify (parse suspects))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GOAL
;; CONVERT THIS:
; "Edward Cullen,10\nBella Swan,0\nCharlie Swan,0\nJacob Black,3\nCarlisle Cullen,6\n\n"
;
; INTO THIS (via parse):
;(["Edward Cullen" "10"]
; ["Bella Swan" "0"]
; ["Charlie Swan" "0"]
; ["Jacob Black" "3"]
; ["Carlisle Cullen" "6"])
;
;; AND THEN INTO THIS (via mapify):
;({:name "Edward Cullen", :glitter-index 10}
; {:name "Bella Swan", :glitter-index 0}
; {:name "Charlie Swan", :glitter-index 0}
; {:name "Jacob Black", :glitter-index 3}
; {:name "Carlisle Cullen", :glitter-index 6})
;; NB vecs -> maps & string-values -> number-values

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Re: mapify
; This:               (map vector vamp-keys ["Edward Cullen" "10"])
; evaluates to this:  ([:name "Edward Cullen"] [:glitter-index "10"])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; QUESTION: WHAT are the ARGS for these PARAMETERS:
;            [row-map [vamp-key value]]?
;(reduce (fn [row-map [vamp-key value]]
;          (assoc row-map vamp-key (convert vamp-key value)))
;        {}
;        ([:name "Edward Cullen"] [:glitter-index "10"]))

andy.fingerhut23:04:09

@johnmi A little experimentation with reduce on a different function may be helpful to understand.

user=> (defn print-args-then-conj [x y]
(println "x=" x "y=" y)
(conj x y))
#'user/print-args-then-conj
user=> (reduce print-args-then-conj [] [5 7 11 13])
x= [] y= 5
x= [5] y= 7
x= [5 7] y= 11
x= [5 7 11] y= 13
[5 7 11 13]

andy.fingerhut23:04:55

Also the doc string for reduce , which you can see via (doc reduce) at a REPL prompt, may be useful?

John MacIsaac23:04:47

Thank you @andy.fingerhut. I just discovered that there is a channel for #bravendtrue and will move my question and your answer over to it. And will close the question for this channel.

andy.fingerhut23:04:19

No problem. Your question is definitely relevant for the #beginners channel, too

John MacIsaac23:04:46

I just dont want abuse slackers with duplicate postings. Altho beginners probably gets more traffic. thanks again.