Fork me on GitHub
#beginners
<
2019-10-31
>
Jason Kimmerling09:10:30

Do you guys think I should learn Clojure as my "first language" (I have started to learn a lot of languages before, but have puttered out several times - PHP being the only one I learned with any depth, and that was ~10+ years ago), or should I go with a lower level language (in my case I would probably go Rust). I have started trying to learn clojure, but the nested functions and parenthesis is getting rather confusing. I want to try to stick it out, but I am definitely not having any fun. I do not know if it is the book I am using or what, but I am left in the dark most of the time as to why things are done the way they are.

Adrian Smith20:11:39

Hey Jason, I had this same issue several years ago, my recommendation for this is to enable "parinfer" in your editor, I think it's called something else in cursive

Alexander Heldt10:10:58

there are different schools on where you should start, low or high level. i personally suggest something like python where you can start grasping data structures and program structures and handle types, memory etc. at a later point to not overload yourself

octahedrion10:10:06

@jkimmerling do what makes you happy

2
Mno10:10:00

I'm in the octahedrion camp, do what makes me happy. People like and learn differently. If you really like the ideas behind a certain language, maybe stick it out a bit more til you understand why they do things. Clojure is a simple language, but understanding why it's a simple language requires a bit of experience so sometimes it's not the easiest to start with.

Mno10:10:08

That being said, some people have had a very good response teaching absolute beginners clojure when the program is specialized for it.

bcaccinolo10:10:13

@jkimmerling you should try this to get an idea of the language https://exercism.io/tracks/clojure

Jason Kimmerling10:10:43

Thanks, that looks quite interesting. I plan to try it out ASAP.

Jason Kimmerling11:10:53

I signed up, but I can only do it in practice mode, as it is full.

bcaccinolo11:10:09

the same. No mentor for Clojure … But it’s still interesting to do.

naproxen10:10:42

(defn make-polyline [geom]
  (let [obj (ol-format/Polyline. (clj->js {:factor 1000000}))
        trick (.readGeometry obj geom (clj->js
                                       {:dataProjection 'EPSG:4326' 
                                        :featureProjection 'EPSG:3857'})) ]
    obj))

(defn add-route [map-id route]
  (let [vector-source (get-map-vector map-id)
        stroke (ol-style/Stroke. (clj->js {:width 6 :color "#e3e363"}))
        style (ol-style/Style. (clj->js {:stroke stroke}))
        polyline (make-polyline route.geometry)
        feature (ol/Feature. (clj->js {:type "route" :geometry polyline}))    <<<---- This is Line55
        ]
    (do 
      (js/console.log polyline)
      (.setStyle feature style)
     (.addFeature vector-source feature))))
TypeError: target.addEventListener is not a function
    at listen (events.js:68)
    at Feature.handleGeometryChanged_ (Feature.js:282)
    at Feature.Target.dispatchEvent (Target.js:162)
    at Feature.BaseObject.notify (Object.js:212)
    at Feature.BaseObject.set (Object.js:233)
    at Feature.BaseObject.setProperties (Object.js:248)
    at new Feature (Feature.js:167)
    at Object.tools$mapping$add_route [as add_route] (mapping.cljs:55)
    at eval (mapping.cljs:63)
    at Object.sval (core.cljs:3422) 
Did anyone worked with OpenLayers I have problem creating with polyline features. I just get such error and I don't know how to investigate deeper. When I have such errors It's super hard to find the real problem what kind of debugging approach you have for such problems?

manutter5111:10:29

@jkimmerling Definitely do what makes you happy, but if you do decide to go with Clojure, there's lots of folks in this channel who would be happy to answer any questions or explain any features you found confusing. Most of us have come from non-Lisp backgrounds, so we're familiar with the brain cramps that can happen when first learning functional programming. Also let us know what book(s) you're using to try and learn--some are better for beginners than others. Clojure for the Brave and True is one that is frequently recommended, and it's free online, so you can easily access it if it's not the book your reading now (https://www.braveclojure.com/). And good luck with your learning!

Jason Kimmerling11:10:56

@manutter51 I am actually using Clojure for the Brave and True, but I started getting lost when they started nesting several functions inside each other.

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

(def vamp-keys [:name :glitter-index])

(defn str->int
  [str]
  (Integer. str))

(def conversions {:name identity
                  :glitter-index str->int})

(defn convert
  [vamp-key value]
  ((get conversions vamp-key) value))

(defn parse
  "convert a CSV into rows of columns"
  [string]
  (map #(clojure.string/split % #",")
       (clojure.string/split string #"\r")))

(defn mapify
  "Return a seq of maps like {:name \"Edward 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))

(defn glitter-filter
  [minimum-glitter records]
  (filter #(>= (:glitter-index %) minimum-glitter) records))
The mapify function had my brain hurting pretty badly lol.

manutter5112:10:52

Ok, one thing I find helpful sometimes is to just take those anonymous functions and pull them out into their own named function. If you’re not used to reading Clojure functions “from the inside out,” (or even if you are!) this can make things a lot more explicit. So here’s mapify with the functions rolled out:

(defn convert
  "Converts CSV values to either int or string, depending on what column
  they're in."
  [the-key the-value]
  (case the-key
    :name (str the-value)
    :glitter-index (Integer. value)))

(defn add-to-map
  "Used in adding CSV values to a map. Takes the map you want to add
  to, the key to store the value under, and the value itself."
  [the-map the-key the-value]
  (let [the-converted-value (convert the-value)]
    (assoc the-map the-key the-converted-value)))

(defn add-csv-row-to-map
  "Given a list of keys and a list of values, return a map with the values mapped
  to their corresponding keys."
  [the-values]
  (let [the-keys [:name :glitter-index]
        the-kv-pairs (map vector the-keys the-values)]
    ;; the above pairs up each key to its corresponding value so we can use
    ;; reduce to add each k/v pair to the map, starting with an empty map.
    (reduce add-to-map {} the-kv-pairs)))

(defn convert-csv-rows-to-maps
  "Given a list of rows from a CSV file, return a list of maps, each of which
  has the row values mapped to the correct key."
  [csv-rows]
  (map add-csv-row-to-map csv-rows))

Jason Kimmerling12:10:41

Very nice! I will dig into that as soon as i get out of this meeting lol

AlesH08:11:34

If this does what I think it does, you can pretty much use zipmap like this

(defn- objects-row->map [object-row]
  (-> (zipmap [:id :material :layer :layer-parent :count-vertex :count-face] object-row)
      (update :count-vertex #(u/parse-number %))
      (update :count-face #(u/parse-number %))))

(map objects-row->map rows)
I came to clojure after 5 years of using java at work. I felt dissappointed and even annoyed by endlessly trying to transform data in an idiomatic way. However, I'm not certain if I would appreciate things like immutability and pure functions if Clojure was my "first language" as I do now. Also it is certainly a good thing to have some low-level knowledge, although if you are motivated to learn things, you can read about, memory, pointers and data structures while learning a language that does that for you.

Byron Miller15:11:09

this was awesome, thx btw!!

Jason Kimmerling11:10:37

Doing the resolution of internal functions had me a bit confused as to what was actually happening

manutter5111:10:02

Yeah, anonymous functions are fun (for some definitions of “fun” 😉 )

Jason Kimmerling11:10:30

I guess i just need to hunt down more examples,s o i can actually know what is really going on

manutter5111:10:49

Give me a second here, maybe I can help parse that out a bit

Jason Kimmerling11:10:09

That would be quite nice of you 🙂

raymcdermott11:10:42

not sure if this a beginners question but I can't find an answer on SO so here goes

raymcdermott11:10:13

I want to hint that the return type of a function is a Java byte array

raymcdermott11:10:25

;; TODO how else can we extend Clojure type system to understand `bytes`?
(def bytes-type (type (.getBytes "")))

(defn ^bytes-type base64-decode
  "Decode a base64 string to bytes"
  [^String str]
  (-> (Base64/getDecoder)
      (.decode str)))

raymcdermott11:10:49

this will avoid the need for reflection on the call site

raymcdermott11:10:18

on the input side we can hint like this

raymcdermott11:10:37

(defn- ^String base64-encode*
  [^bytes bytes-to-encode]
  (-> (Base64/getEncoder)
      (.encode bytes-to-encode)
      (String. StandardCharsets/UTF_8)))

raymcdermott11:10:14

using ^bytes but I get a compile failure if I use it as a return hint

raymcdermott12:10:10

Syntax error (IllegalArgumentException) compiling new at (test/crypto_test.clj:43:54).
Unable to resolve classname: [email protected]

raymcdermott12:10:43

so I have a solution by using this

raymcdermott12:10:13

(def bytes-type (type (.getBytes "")))

raymcdermott12:10:22

as the return type

raymcdermott12:10:47

but am wondering if there is a more elegant / idiomatic option

thheller12:10:30

don't return type hints go on the args vector instead of the name?

thheller12:10:07

(defn foo ^long [^long n]) is in the example

flefik12:10:01

From the link above, > For function return values, the type hint can be placed before the arguments vector: (defn hinted-single ^String [])

flefik12:10:09

like so:

(defn base64-decode 
  "Decode a base64 string to bytes"
  ^bytes [^String str]
  (-> (Base64/getDecoder)
      (.decode str)))

raymcdermott12:10:02

eesh - good spot

flefik12:10:32

most (all?) of the primitives have nicer type aliases

raymcdermott12:10:01

yeah, I was blinded by the other code that has type hints in the wrong place 🤷:skin-tone-3:

raymcdermott12:10:46

so quite a few birds will be killed with this stone (s/birds/bugs)

raymcdermott13:10:37

seems like it's different in CLJS 🙂

lepistane14:10:34

hi guys i've got few questions about machine learning i think (i am not completely sure if that's the right topic) so i made an app that is used for gps tracking. Currently the only job of the app is to send it's current location and some additional data (flags) that user can specify. Interesting topic came up where i would want to predict how much time it would take to get to point A from phone's current position (something like google directions offers u chose from and to and it gives u option by car 3min). I looked online and saw google distance matrix and few alternatives (all of which are not free or have very limited free tier) Then i thought this would be cool machine learning project right? I'd want to create system that correctly predicts how much it will take to get to point A for known route based on time of the day, time of the year, weather forecast and that this model would keep training itself as position changes. Is this something doable? How would i go about doing this?

Alex Young15:10:34

My first reaction was "don't bother, the map data requirement alone will scupper it" but then I realised you might not need it. The way I'd do it would be to divide the world into a hex grid (something like a 5m resolution might do the trick), and for each journey taken, associate a transit time and a direction through each tile. To route to a new location, you'd need there to be a valid route through the grid passing only through tiles which each had a valid transit. That's an A* search, or similar. Then the predicted journey time becomes the sum of the predicted transit times of each tile in the route, and that's a fairly nicely constrained ML problem, given data. Getting enough data into the model to make machine learning any better than "how long did it take last time" is an exercise for the reader who presumably doesn't already have a global distribution of GPS-enabled sensor devices to hand.

Alex Young15:10:29

I have also just remembered that OpenStreetMap exists. Might be worth taking a look at https://routing.openstreetmap.de/about.html and see how it works

Justin Heyes-Jones16:10:31

@lepistane I think that's an interesting application of ML. The parameters would be time, day of week, weather as you mentioned and I would also include holidays. The input data would be hard to get. Ideally you could just query google maps to get the estimate every 30 minutes and save it, but getting the historical data will be an issue. I'm pretty sure this would predict pretty well the journey time. Your problem is "black swan events" that may be quite frequent. Construction, accidents, demonstrations, wildlife, flooding, earthquake, fire... some these events happen quite often and it means that a model based only on the past can only be so useful. I believe Google Maps etc use real-time traffic data to adjust their timings.

Jason Kimmerling16:10:44

For a person trying to learn Clojure as his/her "first language", would it make sense to try to learn emacs for use as the IDE, or should I go with something like Atom?

dpsutton16:10:28

i commit to CIDER a lot and i would not recommend learning programming and emacs at the same time

seancorfield16:10:03

@jkimmerling A person who has never used any sort of editor yet? I'd say use something that doesn't require much of a learning curve, i.e., not-Emacs.

dpsutton16:10:25

^ agreed. the simplest thing so that the only thing you're focusing on is Clojure and programming, not your editor

seancorfield16:10:46

Atom is fine -- but ProtoREPL, the nREPL client package, is unmaintained at this point so you'd end up with Chlorine (which is awesome!) and that "only" works with a Socket REPL, not nREPL, so it's confusing for a complete newcomer who is trying to follow tutorials that use lein repl etc.

seancorfield16:10:19

I think for a complete beginner, using Nightcode would probably be a good place to start. Followed by VS Code and Calva.

calva 1
Jcaw16:10:39

Emacs is difficult to use without knowing how to program Elisp, if nothing else because it's just not that stable. You end up needing to fix your own bugs.

Jcaw16:10:45

I.e. you'll be learning two languages, not just an editor and a language

Jason Kimmerling16:10:03

Thanks all, I'll probably avoid emacs for now. @seancorfield thanks for pointing that out, I did not know ProtoREPL was no longer maintained

seancorfield17:10:53

Last release was about 18 months ago and it's broken with recent versions of Ink -- so it's quite fiddly even getting a working setup these days with ProtoREPL. Sadly.

donyorm17:10:26

Is there a function that takes a seq of values and returns true if any of the values in the seq are true?

bfabry17:10:32

(some true? seq)

donyorm17:10:20

Ok I was using something along those lines but it seemed a little clunky. Good to know that's what's recommended. Thanks!

donyorm17:10:09

Oh, I guess to clarify I meant truthy, not necessarily true (so I was using (some identity coll)). Is that what's recommended?

noisesmith17:10:59

that's precisely it, yeah

noisesmith17:10:23

I often wish that partial and identity had shorter names

bfabry17:10:38

(some some? coll) amusingly

bfabry17:10:03

identity works too, so long as you don’t specifically want a boolean out

noisesmith17:10:14

@bfabry some? is true for false though

bfabry17:10:31

doh, you’re right

noisesmith17:10:36

(some boolean coll) is for true / false

noisesmith17:10:13

some might consider (boolean 0) -> true a gotcha though

1
😛 1
Andrea Imparato21:10:51

a recommendation for all the beginners like me who are interested in fullstack programming with clojure: have a look at https://github.com/coast-framework/coast it’s awesome and super simple to use and works really well out of the box!

Mattias22:10:15

Hey, basic inspiration needed. I have a structure like ({:key 1 :db-thing “content”} ...) and need to produce a JSON like [{data: {key: 1, db-thing: “content”} ...]. For reasons. (Sorry about the formatting, really can’t grok iOS Slack.) What’s a good way?

lilactown22:10:12

@mattias504 if you’re using Clojure on the JVM, try https://github.com/clojure/data.json If you’re using ClojureScript, there’s a function in cljs.core clj->js

👍 1
Darrell23:10:05

This seems pretty simple but I haven’t been able to put my finger on it yet. I have a call like (jdbc/insert! tx :my_types {:type "Partner"}) which returns ({:created_on #clj-time/date-time "2019-10-31T23:37:47.752Z", :id 5, :type Partner}) if I (println) the jdbc call. What can’t seem to figure out is how to store :id so that I can use it in a subsequent function call?

dpsutton23:10:20

are you familiar with let?

dpsutton23:10:56

(let [inserted (jdbc/insert! tx :my_types {:type "Partner"})]
  (other-function (map :id inserted)))

Darrell23:10:17

Yes but putting that call in let didn’t seem to get me what I wanted. I was doing something like: (let [{my-id :id} (jdbc/insert! tx :my_types {:type "Partner"})]) but then I get this error:

Execution error (IllegalArgumentException) at test.dev/load-stuff$fn (form-init6417694814297120209.clj:41).
No value supplied for key: {:created_on #clj-time/date-time "2019-10-31T23:45:12.500Z", :45:12.500Z", :id 9, :type "Partner"}

bfabry23:10:48

jdbc/insert! appears to return a list, not a map

bfabry23:10:04

you could do the destructure like [{my-id :id}]

Darrell23:10:26

Perfect! Thank you @bfabry!

Darrell23:10:41

Ah! I was trying to use :id prematurely then, eh?

dpsutton23:10:43

yeah. notice my use of (map :id inserted). since insert is returning a sequence of inserted values