Fork me on GitHub
#beginners
<
2021-02-15
>
Joe10:02:23

I've written a function that takes a lazy sequence and returns a lazy sequence of the partial sums (i.e. the accumulated sum of all terms to that point). I came up with this but it feels like there's a more direct way to do it. Am I missing something?

(defn partial-sums
  ([nums] (partial-sums 0 nums))
  ([acc [fst & rst]]
   (let [new-acc ((fnil + 0) fst acc)]
     (cons new-acc (lazy-seq (partial-sums new-acc rst))))))

chrisblom10:02:40

(reductions + [1 2 3 4])

nice 3
❤️ 3
Joe10:02:56

Perfect, thanks!

Tim Robinson13:02:11

Can anyone explain the difference between and and when ? they sound like different concepts but seem to do the the same thing (i.e. evaluate and return the second arg only if the first one is true)

manutter5113:02:29

Not quite. and is a “short-circuiting” boolean AND operation that returns “truthy” if all its arguments are truthy, and when more like an if without an else--if the first argument to when is true, it evaluates all the rest of its arguments regardless of whether or not they return a truthy value. (Where “truthy” means any non-nil value).

manutter5113:02:32

So if you have (and a b c d e) and a and b are true and c is nil, and will return false without ever evaluating d and e. (when a b c d e) will evaluate all 5 arguments, as long as a returns a truthy value.

manutter5113:02:27

A slightly more interesting example:

(and true 1 "ok" nil (throw (Exception. "boom")))
=> nil
(when true 1 "ok" nil (throw (Exception. "boom")))
Execution error at user/eval36922 (form-init10506123581459132295.clj:1).
boom

Tim Robinson13:02:24

yes good point, I was only thinking about the simple case of (when a b)

Tim Robinson13:02:32

Also (and false x) returns false whereas (when false x) returns nil (though that often wouldn't matter)

manutter5114:02:02

Interestingly,

(and false true)
=> false
(and nil true)
=> nil

manutter5114:02:42

So technically if and finds a falsy value, it immediately returns the falsy value and does not evaluate any further args.

grazfather14:02:57

I got a strange error: I was running something in a loop for about a day with a Thread/sleep in it. It was working great, but then it randomly crashed

{:clojure.main/message
 "Syntax error reading source at (tempplot/core.clj:21:1).\nEOF while reading, starting at line 16\n",
 :clojure.main/triage
But the file never changed, how in the world is this possible?
(defn create-db
  [db]
  (try (db-do-commands
          db (create-table-ddl :temps
                               [[:timestamp :datetime :default :current_timestamp ]
                                [:location :text]
                                [:temp :real]]
                               :conditional?))
       (catch Exception e
         (println (.getMessage e)))))
line 16 is the create-table-ddl line

grazfather14:02:18

create-db was already run literally > 24 hours earlier

andy.fingerhut14:02:24

Is there a stack trace after those 3 lines of error you showed? One thing it makes me immediately wonder is "if Clojure loaded that source file earlier, why was it trying to read the source file again later?" Do you have some explicit load or load-file call in your Clojure code that would cause that? Or a (require ...) with :reload-all as an arg?

andy.fingerhut14:02:30

Clojure is definitely a dynamic enough language that you can load source files long after the program begins running, but it is not the most common practice in Clojure programs to do so.

grazfather15:02:58

Here is the trace

grazfather15:02:01

{:clojure.main/message
 "Syntax error reading source at (tempplot/core.clj:21:1).\nEOF while reading, starting at line 16\n",                                                                              :clojure.main/triage
 {:clojure.error/phase :read-source,
  :clojure.error/line 21,
  :clojure.error/column 1,
  :clojure.error/source "core.clj",
  :clojure.error/path "tempplot/core.clj",
  :clojure.error/cause "EOF while reading, starting at line 16"},
 :clojure.main/trace
 {:via
  [{:type clojure.lang.Compiler$CompilerException,
    :message
    "Syntax error reading source at (tempplot/core.clj:21:1).",
    :data
    {:clojure.error/phase :read-source,
     :clojure.error/line 21,
     :clojure.error/column 1,
     :clojure.error/source "tempplot/core.clj"},
    :at [clojure.lang.Compiler load "Compiler.java" 7643]}
   {:type java.lang.RuntimeException,
    :message "EOF while reading, starting at line 16",
    :at [clojure.lang.Util runtimeException "Util.java" 221]}],
  :trace                                                                                                                                                                             [[clojure.lang.Util runtimeException "Util.java" 221]
   [clojure.lang.LispReader readDelimitedList "LispReader.java" 1405]
   [clojure.lang.LispReader$ListReader invoke "LispReader.java" 1243]
   [clojure.lang.LispReader read "LispReader.java" 285]
   [clojure.lang.LispReader readDelimitedList "LispReader.java" 1398]
   [clojure.lang.LispReader$ListReader invoke "LispReader.java" 1243]
   [clojure.lang.LispReader read "LispReader.java" 285]
   [clojure.lang.LispReader read "LispReader.java" 216]
   [clojure.lang.Compiler load "Compiler.java" 7631]
   [clojure.lang.RT loadResourceScript "RT.java" 381]
   [clojure.lang.RT loadResourceScript "RT.java" 372]
   [clojure.lang.RT load "RT.java" 459]
   [clojure.lang.RT load "RT.java" 424]
   [clojure.core$load$fn__6839 invoke "core.clj" 6126]
   [clojure.core$load invokeStatic "core.clj" 6125]
   [clojure.core$load doInvoke "core.clj" 6109]
   [clojure.lang.RestFn invoke "RestFn.java" 408]
   [clojure.core$load_one invokeStatic "core.clj" 5908]
   [clojure.core$load_one invoke "core.clj" 5903]
   [clojure.core$load_lib$fn__6780 invoke "core.clj" 5948]
   [clojure.core$load_lib invokeStatic "core.clj" 5947]
   [clojure.core$load_lib doInvoke "core.clj" 5928]
   [clojure.lang.RestFn applyTo "RestFn.java" 142]
   [clojure.core$apply invokeStatic "core.clj" 667]
   [clojure.core$load_libs invokeStatic "core.clj" 5985]
   [clojure.core$load_libs doInvoke "core.clj" 5969]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.core$apply invokeStatic "core.clj" 667]
   [clojure.core$require invokeStatic "core.clj" 6007]
   [clojure.core$require doInvoke "core.clj" 6007]
   [clojure.lang.RestFn invoke "RestFn.java" 408]
   [user$eval140$fn__144 invoke "form-init798709166106965070.clj" 1]
   [user$eval140 invokeStatic "form-init798709166106965070.clj" 1]
   [user$eval140 invoke "form-init798709166106965070.clj" 1]
   [clojure.lang.Compiler eval "Compiler.java" 7177]
   [clojure.lang.Compiler eval "Compiler.java" 7167]
   [clojure.lang.Compiler load "Compiler.java" 7636]
   [clojure.lang.Compiler loadFile "Compiler.java" 7574]
   [clojure.main$load_script invokeStatic "main.clj" 475]
   [clojure.main$init_opt invokeStatic "main.clj" 477]
   [clojure.main$init_opt invoke "main.clj" 477]
   [clojure.main$initialize invokeStatic "main.clj" 508]
   [clojure.main$null_opt invokeStatic "main.clj" 542]
   [clojure.main$null_opt invoke "main.clj" 539]
   [clojure.main$main invokeStatic "main.clj" 664]
   [clojure.main$main doInvoke "main.clj" 616]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.main main "main.java" 40]],
  :cause "EOF while reading, starting at line 16",
  :phase :read-source}}

grazfather15:02:24

(ns tempplot.core
  (:require [dvlopt.linux.i2c :as i2c]
            [dvlopt.linux.i2c.smbus :as smbus]
            [clojure.data.json :as json]
            ;[oz.core :as oz]
            [clojure.java.jdbc :refer :all]))

(def db
  {:classname "org.sqlite.JDBC"
   :subprotocol "sqlite"
   :subname "db/database.db"})

(defn create-db
  [db]
  (try (db-do-commands
          db (create-table-ddl :temps
                               [[:timestamp :datetime :default :current_timestamp ]
                                [:location :text]
                                [:temp :real]]
                               :conditional?))
       (catch Exception e
         (println (.getMessage e)))))


(defn minimum-legal-temp
  [datetime outside-temp]
  ; TODO: Add date awareness: Heat season is between Oct 1 and ??
  (if (and (>= (.getHour datetime) 6) (< (.getHour datetime) 22))
    (if (< outside-temp 12.78) ; Day time, if under 55°F, then keep 68°F
      20
      outside-temp)            ; Otherwise there's no miniimum
    16.67))                    ; At night the minimum is always 62°F during heat season

(comment
  (minimum-legal-temp (java.time.LocalDateTime/of 2021 2 13 5 45) 10)
  (minimum-legal-temp (java.time.LocalDateTime/of 2021 2 13 15 45) 10)
  )

(def reg-temp 0)
(def reg-config 1)
(def resolution 0.0078125)

(defn bytes->val
  [b]
  (reduce (fn [acc v] (+ v (* acc 256))) b))

(bytes->val [8 201])

(def reading->C (partial * resolution))

(defn read-reg
  [bus reg]
  (i2c/transaction bus
        [{::i2c/slave-address 0x48
          ::i2c/write [reg]}
         {::i2c/slave-address 0x48
          ::i2c/read 2}]))

(defn line-plot
  [db]
  {:data {:values (doall (query db ["select * from temps order by timestamp"]))}
   :encoding {:x {:field "timestamp" :type "nominal"}
              :y {:field "temp" :type "quantitative"}
              :color {:field "location" :type "nominal"}}
   :mark "line"})


(def api-host "")
(def api-key "xxx")
(def api-current-weather "")
(def manhattan-city-id 5125771)

(defn get-current-temperature
  [city-id]
  (-> (format api-current-weather manhattan-city-id api-key)
      slurp
      json/read-str
      (get-in ["main" "temp"])))

(defn get-manhattan-temp [] (get-current-temperature manhattan-city-id))

(defn -main
  [& args]
  (println "Creating db")
  (create-db db)
  (println "Reading temps")
  (with-open [bus (i2c/bus "/dev/i2c-1")]
    (while true
    ;(dotimes [i (* 12 18)] ; for now
      (let [inside-temp (-> (read-reg bus reg-temp)
                  (get 1)
                  bytes->val
                  reading->C)
            outside-temp (get-manhattan-temp)
            now (java.time.LocalDateTime/now)
            min-temp (minimum-legal-temp now outside-temp)]
         (insert! db :temps {:location "inside" :temp inside-temp :timestamp now})
         (insert! db :temps {:location "outside" :temp outside-temp :timestamp now})
         (insert! db :temps {:location "minimum" :temp min-temp :timestamp now})
         (println (format "%s inside %.2fC outside %.2fC illegal? %s" now inside-temp outside-temp (< inside-temp min-temp))))
      (Thread/sleep (* 5 60 1000)) ; 5 minutes
    )
  )
  (spit "plot.edn" (line-plot db))
)

; {1 [8 201]}
; {1 [0 240]}

andy.fingerhut15:02:27

Do you know of any way that your program would try to load new Clojure source code long after it started?

andy.fingerhut15:02:58

Or was this exception perhaps caused because you had a live REPL session open with the running program, and you did something in the REPL that tried to load Clojure source code?

andy.fingerhut15:02:01

I'm only eyeballing it, but I see nothing about that source file that ends at line 21 🙂

andy.fingerhut15:02:48

Is there any chance that you were editing the source file after your program was already running?

grazfather15:02:45

no repl, no editing the file

grazfather15:02:53

it was on a raspberry pi I hadn’t touched

grazfather15:02:56

I just checked it this morning

grazfather15:02:14

and in fact the time stamp on the file was 13 hours before the error

manutter5116:02:16

Maybe the Pi itself reset for some reason? Would your program relaunch if the device rebooted?

noisesmith16:02:29

how did you run clojure? via java directly, with clj, lein?

hiredman16:02:37

The file(form whatever.clj) name in a the stack trace means lein

noisesmith17:02:18

so likely "lein run", I wonder if there's a hidden reloading plugin somewhere

hiredman17:02:29

you should look in the database to see what information was recorded in there, since it includes timestamps, my guess is you'll find less information than you expect, indicating that the error happened much earlier then you think

andy.fingerhut18:02:17

It does seem a lot more plausible that (a) the exception you showed actually occurred shortly after starting you program, perhaps because the file was not fully copied to the Raspberry pi where it was running somehow, but you did not notice it until much later, versus (b) the file was fully there when you started your program, got loaded once successfully then, your program for some reason tried loading it again 13 hours later (same running JVM process), and the file was somehow changed in a way that the modify time stamp did not get updated.

grazfather00:02:43

it’s worse than all of these. Sorry for setting you guys on a wild goose chase

grazfather00:02:54

I had opened the wrong stack trace facepalm

grazfather00:02:25

The problem is that I am doing a format "…%.2f" (api call) and when parsing the response json if the temperature happens to be exactly zero then the type is not a float

andy.fingerhut00:02:10

glad you found the actual problem!

Adrian Imanuel17:02:46

Hi, newbie question here, I'm using SQL server express, and some test database "AdventureWorks", but got an error java.sql.SQLException: No suitable driver found for jdbc:;DATABASENAME=AdventureWorks I've installed my jdbc via com.microsoft.sqlserver.jdbc.SQLServerDriver class path environment C:\Program Files\Java\jdk-15.0.2\mssql-jdbc-9.2.0.jre15.jar my code is

(ns clojuresql.core
  (:require [next.jdbc :as jdbc]
            [hugsql.core :as hugsql]
            [hugsql.adapter.next-jdbc :as adapter]
            [next.jdbc :as jdbc]
            [honeysql.core :as sql]
            [honeysql.helpers :refer :all :as helpers]))

(def db-sqlserver {:dbtype "sqlserver" :dbname "AdventureWorks"
                   :user "sa" :password "123456789"})

(def ds (jdbc/get-datasource db-sqlserver))


(jdbc/execute! ds
               ["show tables"])
where did i do wrong?

noisesmith17:02:41

@adrianimanuel you don't "install" a jdbc driver, you add it as a maven dep to a project

noisesmith17:02:06

(using any mainstream clojure option like lein or deps.edn at least)

noisesmith17:02:42

you can check the output of (System/getProperty "java.class.path") to see if mssql-jdbc is mentioned anywhere

Adrian Imanuel17:02:10

@noisesmith oh, okay it's like this right com.h2database/h2 {:mvn/version "1.4.199"} , but for the SQLserver, from here https://mvnrepository.com/artifact/com.microsoft.sqlserver/mssql-jdbc/9.2.0.jre15 [com.microsoft.sqlserver/mssql-jdbc "9.2.0.jre15"] am i right?

noisesmith17:02:57

right - that's the normal way to do it - the general concept is that there's no global state of installed libs, but rather a config of which libs and versions you want, and a resolver that provides them to your process

👏 3
Adrian Imanuel17:02:17

ahh okay, noted... thanks so much!

noisesmith17:02:22

this is weird at first! but now I find every other programming environment primitive. what do you mean I need to create a whole virtual OS / user environment just to use different lib versions in two projects?

noisesmith17:02:52

a small upfront complexity to remove the avalanche of complexities that global installs create...

3
noisesmith17:02:47

@adrianimanuel one more note - you have two sample lib specs there, the h2 example is in deps.edn format, the mssql one is in lein format - the two are easy to convert in your head but definitely make sure you have the right one

Adrian Imanuel17:02:35

yes, i read h2 in deps.edn format from https://github.com/seancorfield/next-jdbc/blob/develop/doc/getting-started.md But, noted, I'm using lein format to do that. I've got another error here, it's already connected but what should i do?

Execution error (SQLServerException) at com.microsoft.sqlserver.jdbc.SQLServerException/makeFromDriverError (SQLServerException.java:234).
The TCP/IP connection to the host 127.0.0.1, port 1433 has failed. Error: "Connect timed out. Verify the connection properties. Make sure that an instance of SQL Server is running on the host and accepting TCP/IP connections at the port. Make sure that TCP connections to the port are not blocked by a firewall.".

Adrian Imanuel17:02:40

i've also create inbound exception in the firewall for 1433 port

dpsutton17:02:56

are you running things in WSL?

Audrius18:02:39

How to kill the current command in the REPL? I have something like (javadoc Object) and I want to kill it is there a way? (it is "lein repl")

Adrian Imanuel18:02:16

ctrl + c twice maybe?

Audrius18:02:53

did not work

Adrian Imanuel18:02:27

thats shortcut to kill in emacs

noisesmith19:02:27

wait, why would javadoc hang? it just shells out and returns immediately in my experience

noisesmith19:02:00

nrepl, which lein uses, has a signal handler that kills the current top level execution on control-c

noisesmith19:02:02

if javadoc hangs (maybe because your browser is set up weird?) that sounds like a bug in javadoc

andy.fingerhut22:02:10

If the person asking is using a Linux system with xdg-open installed, it might be related to this issue: https://clojure.atlassian.net/browse/CLJ-2493

noisesmith22:02:20

oh wow - I never had xdg-open hang on me, I guess because I already had a browser running so it just opens a tab in that browser an returns immediately without the need to start a process

noisesmith22:02:36

but yeah, good find, definitely a bug in javadoc

Audrius07:02:02

yes this hangs only if javadoc opened a new browser

andy.fingerhut11:02:48

It seems that a workaround for that issue is to start the default browser first.

noisesmith14:02:30

this should be a simple fix, as surely nobody uses javadoc in a critical path, it's easy to manage the stdio of a child with ProcessBuilder

andy.fingerhut16:02:28

There is a patch on the ticket I linked that led to improved behavior in my interactive testing. Alex Miller's last comment on it that I recall was that he'd like to think about it more and see if there is a better way. I do not know what the criteria for better is here.

em20:02:10

What's the rule on evaluation for vars that refer to vars? Might be a weird question, but I'm curious about why this works:

(def a inc)
(def b #'a)
(def c #'b)

@#'a
;; #object[clojure.core$inc 0x2b39a1 "[email protected]"]
@#'b
;; #exercises.core/a
@#'c
;; #exercises.core/b

;; However, c still works as a function
(c 4)
;; 5
I kinda expected a function call to c would evaluate to the var b, and not the function object inc all the way down the chain. Does evaluation explicitly continuously unwrap vars until something that implements IFn , or instead is it that vars can function as functions that pass arguments down to whatever they're pointing at, thus c in the above example is just a bunch of nested function calls?

noisesmith20:02:57

@eagonmeng when you call a var, it derefs and calls the thing it contains

noisesmith20:02:04

refs do this too

noisesmith20:02:06

it's not the evaluators job per se - var implements the IFn interface

user=> (supers clojure.lang.Var)
#{clojure.lang.Settable java.lang.Runnable clojure.lang.IFn java.util.concurrent.Callable java.io.Serializable clojure.lang.IDeref clojure.lang.IMeta clojure.lang.IRef clojure.lang.AReference java.lang.Object clojure.lang.ARef clojure.lang.IReference}

noisesmith20:02:07

the evaluator takes the list (x) and since in most contexts () means call, it looks for the way to call the thing

noisesmith20:02:35

so if x is a var, it finds that the var is an IFn (a first class thing clojure knows how to call), and invokes it

noisesmith20:02:43

the object decides what "calling" means

noisesmith20:02:11

in the var case, calling means calling the thing it contains, but really it's up to the object, it's not in clojure's compiler's control

noisesmith20:02:28

using some OO primitives really helps here I think:

(cmd)user=> (def f (reify clojure.lang.IFn (invoke [this] "hi")))
#'user/f
(ins)user=> (f)
"hi"

noisesmith20:02:06

reify creates a thing that implements some interface, here it creates a thing clojure knows it can call - anything could go in that method

em20:02:54

@noisesmith Thanks for the clarification! Makes sense, so it was the latter option I was thinking about. A lot more clean than baking in some weird unwrapping mechanism in evaluation, which was what I was confused about. Essentially vars ARE functions since they implement IFn , and they simply call the thing the thing they point at when treated as such, and thus sequentially referenced vars form a call chain until they hit an actual function or error out when they resolve to something that doesn't.