Fork me on GitHub

I’ve been using a lot of Elixir, and in that language you can use pattern matching to dispatch to different implementations of a function depending on the arguments. In my case right now, I just want to dispatch based on whether the first arg is a string or a list. What’s the standard way to do this?


What is what I’m talking about called?


polymorphic dispatch?


Both multimethods and protocols seem like too much for what I’m trying to do. I assume there is something simpler?


Is this preferred for something this simple?

(defn something [arg]
    (str? arg)  arg
    (list? arg) (other-fn arg)))


that is fine, although it is string? not str? and seq? or sequential? is likely what you want not list?


but I would avoid that kind of thing, a function where an arg can be different types


isn’t that the essence of a dynamically typed language?


or you mean just dispatching based on type?


I mean it is weird to have a function that takes a string or a list as the same argument

David Pham00:11:07

multi method seems the right choice though

👍 4

the only thing I would thing if is some really annoying apis will return a string value instead of a list containing one string


that’s my situation


sometimes it’s a string, sometimes its a list of strings


I would undo that as soon as possible, meaning take the api response and box it in a vector if it is not already a list


and then everything after it is fixed up doesn't have to worry about it


I guess I’ll do that, but I was more interested in learning a general lesson rather than solving this specific problem


and box is a fairly common function to write, and it looks sort of similar to your something function


But maybe that is the lesson


All right thanks for your help guys


ring middleware is kicking my ass 😄


I want to check if the session variables are set, if so give you the app, if not give you the login screen


but I get lots of Null Pointer Exceptions ;-;


@chrysalis129 feel free to ask your beginner questions here. I have to ask : is this a class homework problem? I've seen several people ask for help with the exact same problem in various places.

Richard Desouza02:11:02

yes it is its an assignment due on sunday 😧

Richard Desouza02:11:46

if anyone can help me with debugging this that would be great


That explains why your class mates are all posting the same question then 😁


I'm eating right now but will be happy to help later -- or any time over the weekend

Richard Desouza02:11:04

ok sounds good 🙂


Seems like everyone is struggling with it. Makes me think maybe the teacher isn't doing a very good job ...

Richard Desouza02:11:16

yea the prof usually only teaches syntax when it's not relevant to an assignment.. plus he often goes on tangents during lectures so it's hard to pay attention

Richard Desouza02:11:35

first assignment he gave = super easy and now this one super hard 😧


I sympathize -- my first year comp Sci prof was terrible 🙃


Is it working but taking too long?


And what are y’all going over in class?

Richard Desouza02:11:34

it's not i got most of the functions to work but cities doesnt print all the csv values anymore and i'm trying to find a way to do closest-city-pairs without the use of ->>

Richard Desouza02:11:23

also theres another function i need to implement

Richard Desouza02:11:41

its to take all the :population key values from all the hashmaps in cities and sum them up some how

Richard Desouza02:11:12

im trying to map the populations to a vector but it doesnt seem to want to print too

Richard Desouza02:11:13

(defn total-population [cities] (for [x (map :pop (cities)) :let [y x]] (print y)))


You have parens around cities -- are you passing in function?

Richard Desouza02:11:32

cities returns the hash map of each row in the csv file


Sorry I’m down to help and explain but I still don’t see a question or what you working. Just the whole file

Richard Desouza03:11:00

ok nevermind i got the total population working

Richard Desouza03:11:09

now for the last function i need to do

Richard Desouza03:11:33

i need to print :province and :pop from all the hash maps in cities and sort them in order from smallest :pop to largest

Richard Desouza03:11:40

any idea where i can start with that?

Richard Desouza03:11:07

how would i go about printing just :province and :pop from cities(returns a vector of all hash maps in a csv file))

Richard Desouza03:11:20

also is there anyway to complete this function without using ->> and (filter large) dpsutton if i might ask?


Why do you want to avoid the threading macro?


And which function?

Richard Desouza03:11:22

Because theres a thread with the solution on reddit and i feel all my classmates are going to copy it so I want to find a different way to do the function

Richard Desouza03:11:28

(defn closest-city-pairs [] (let [y (->> (cities) (filter large) vec)] (for [a (butlast y) b (rest y) :when (and (not= a b) (close a b))] [(:city a) (:city b) (distance a b)])))

Richard Desouza03:11:38

i tried doing it this way


Well you can remove the vec and just use (filter large (cities)) but I think the for loop is the more tell tale expression there

Richard Desouza03:11:08

but i get unsupported binding form :when


What is your current version? Let’s see what’s up with the :when binding error. If I had to guess you’ve got that in the let rather than the for


(I'm back from dinner and have a beer and a warm REPL!)

👍 8

In that for comprehension you're going still compare N-2 of those cities against themselves so I don't think you're saving much with butlast and rest -- you already need (not= a b) so you might just as well do (for [a y b y :when (and (not= a b) (close a b))] [(:city a) (:city b) (distance a b)])


(since close calls distance anyway and just checks it against 600, I'd probably have :let [d (distance a b)] :when (< d 600) inside the for and then [(:city a) (:city b) d] in the body of it)


(or have a predicate near? that takes a distance and check it against 600km: (defn near? [d] (< d 600)) -- then :when (near? d) in the for per above)


Lots of ways to write it.


You can also use (< 0 d 600) and then you don’t have to check not=


Since distance is a metric distance is zero if and only if the points are the same


Per @dpsutton’s comment above, since you're using for and it only cares about sequences, you definitely don't need to call vec on the filtered sequence of cities:

(defn closets-city-pairs []
  (let [large-cities (filter large (cities))]
    (for [a large-cities b large-cities
          :let [d (distance a b)]
          :when (< 0 d 600)]
      [(:city a) (:city b) d])))
Something like that?

Richard Desouza03:11:02

thanks that helps

Richard Desouza03:11:50

okay now for some reason the distance between two cities isnt accurate anymore


Use your repl with two simple cities and investigate

Richard Desouza03:11:57

nevermind it works fine scratch that


You can use Pythagorean triples so the distances are nice easy numbers. (0,0) and (3,4) will give you a distance of 5

Richard Desouza03:11:58

how can i get print out a vector of hash maps that just returns the :province and :population

Richard Desouza03:11:33

if i have cities function that returns the vector with all the hashmaps and keys


select-keys will help


Also, if you want to map a function over a sequence and get back a vector instead of a sequence, you can use mapv (there's a filterv as well)

Richard Desouza03:11:05

ok but i need to iterate over a vector with multiple hash maps.. how do i just get the :pop and :province

Richard Desouza03:11:22

select-key doesnt seem to work if i try to pass (cities) into it


Select keys takes a single map. So you can map it over a sequence of maps


(mapv #(select-keys % [:pop :province]) (cities))


(map inc [1 2 3]) will call inc on each item in the vector. Likewise you want to select keys for each map


@chrysalis129 Some good advice about map and functions (when you're ready to think about idiomatic code as opposed to just working code).

Richard Desouza04:11:15

(print (mapv #(select-keys % [:province :pop]) (cities)))

Richard Desouza04:11:23

it seems to only be returning the :pop for each tho


Are you sure that all the city maps have :province as a key?

Richard Desouza04:11:20

had it as :admin


Administrative Region 🙂


Yeah, I've seen that coming back from Google Geo APIs.

Richard Desouza04:11:22

okay so it prints like this

Richard Desouza04:11:25

{:admin Nunavut, :pop 0}]

Richard Desouza04:11:35

is it possible to get rid of :admin and :pop so it just prints the values?

Richard Desouza04:11:43

also i tried sorting it in terms of population

Richard Desouza04:11:02

(print (mapv #(select-keys % [:admin :pop]) (cities)) (sort-by :pop))

Richard Desouza04:11:34

its giving me wrong number of args passed thgo


(mapv vals coll) will give you sequences of just the values from hash maps


You might not always get them in the same order tho' since hash maps are unordered. If you always want :admin then :pop, you might look at the juxt function


(mapv (juxt :admin :pop) (cities)) will give you a vector of vectors, each subvector containing the :admin value, followed by the :pop value.

Richard Desouza04:11:38

okay yeah that works

Richard Desouza04:11:47

ok it goes from largest population to lowest

Richard Desouza04:11:54

where do i put sort-by to flip it?


You can either sort the city collection, then map over and select just the two fields, or you can do it the other way around.


In the first case you would sort-by :pop since you have hash maps with the key :pop


In the second case you would sort-by second since you now have vectors with the population in the second position


is there any function that can parse 20% to 0.2?


How is 20% represented @haiyuan.vinurs ?


(if it's just an integer -- 20 -- you can just divide by 100, or by 100.0 if you specifically want a double result rather than a ratio)

Richard Desouza04:11:48

ok i tried doing the first option sean

Richard Desouza04:11:56

its saying im giving the wrong number of inptus tho for my function

Richard Desouza04:11:58

(defn provincial-population [] (let [y cities] (sort-by :pop) (mapv (juxt :admin :pop) (cities)) ))


(sort-by :pop coll) <-- needs a collection


Remember that Clojure is a) expression-based and b) all immutable. Sorting produces a new collection that is a sorted version of the input collection.


@seancorfield i read the string 20% from file, so i need to parse it


So you're either going to have (sort-by ... (mapv ... (cities))) or (mapv ... (sort-by ... (cities)))


->> can make the order of operations clearer, if you want.


@haiyuan.vinurs so you have "20%" as a string?

Richard Desouza04:11:21

ok i got it to print

Richard Desouza04:11:28

but its still doing largest populations first

Richard Desouza04:11:31

(defn provincial-population [] (let [y cities] (sort-by :pop (mapv (juxt :admin :pop) (y))) ))


now i want convert the string 20% to float 0.2


@chrysalis129 Read what I said above about the order of sort and select keys. You're trying to sort on :pop after you've produced a vector of vectors.


@haiyuan.vinurs are you guaranteed all the values are <some sequence of digits> always followed by %? If so, you could just do a substring operation to remove the last character, then parse it as a long, then divide by 100.0


(Long/parseLong "20") will produce 20, for example.

Richard Desouza04:11:18

thanks that worked sean

Richard Desouza04:11:28

Much love to you guys here 🙂

Richard Desouza04:11:32

finished my assignment

Richard Desouza04:11:41

this is a great place


Assignments are never "finished" 🙂


Let us know how the marking goes on it.

Richard Desouza04:11:51

I will 🙂 thanks again

Richard Desouza04:11:27

actually im not done my apologies -_-

Richard Desouza04:11:44

i just realized we have to sum up each province because theres hash maps with the same province


You can write a helper fn map-vals that take a fn and a map and returns a new map with the fn applied to each value in the input map. Like this:

(defn map-vals [f m]
  (into {} (map #(update % 1 f)) m))
You then have to group the cities by province and use map-vals to turn those collections of cities into the sum of population, then sort that. Like this:
(->> (group-by :province cities)
     (map-vals #(apply + (map :pop %)))
     (sort-by (comp - val)))
This will give you provinces sorted in decreasing order of population.

Richard Desouza07:11:38

the function isn't allowed to have any parameters unfortunately 😧


Who's restricting you from having parameters on that function?

Richard Desouza04:11:52

and then sort that

Richard Desouza04:11:40

i think ill need a for loop yea?


Excellent time to look into reduce


Or you could look into group-by

Richard Desouza07:11:55

(defn distinct-provincial-population [] (for [[p xs] (group-by :province (cities))] [p (reduce + (map :pop xs))])) is what i got so far

Richard Desouza07:11:00

any ideas how to proceed from there


Sounds about right -- but isn't it :admin rather than :province?


Hi, everyone I keep running into a problem with records and protocols : We have a couple of protocols in separate namespaces, the record implementing those protocols resides in a separate ns. Whenever I uberjar and import it as a lib in another project it gives the exception : *no impelmentation of method ... of protocol ... found for class*. Is there sth I am missing about protocols & records? Does it have to do with which ns s are imported where I am using them?

Alex Miller (Clojure team)13:11:31

Probably. You need to load (require) any namespaces that contain implementations before you can use them


I imported the record and the exception went away. (The namespace containing the implementation was already required). Do I always have to import the record or there is some other thing at work here?

Alex Miller (Clojure team)14:11:19

It depends what you’re doing but you should not generally need to import


I have a middleware for checking logins, I want to check the session variable and redirect to login if it's not set


but i'm stuck in an infinite loop of login checking middleware


actually it seems to work now but i keep getting null pointer exceptions on RegEx matches


I'm having a beginner moment. Why does the following always return "dunno"?

(defn color->name [color]
  (case color
    "#ffffff" "white"
  	"#000000" "black"


Using a switch case in Java "works". How is Clojure seeing the "#" differently?


try it without the dunno and see if you get "no matching clause"


Actually your code works for me when I put in "#ffffff"


jpdrills.server=> (defn color->name [color]
             #_=>   (case color
             #_=>     "#ffffff" "white"
             #_=>       "#000000" "black"
             #_=>     "dunno"))
jpdrills.server=> (color->name "#ffffff")


Ok, so I just copy/pasted it again, and now it's working for me.


not your question, but why not just make a hashmap of it, then use that as a function?


@tws makes a great point, could you provide an example for our new friend?


(def color->name {"#ffffff" "white", ...})


(map color->name color-coll)


you’ll have nil instead of "dunno"


I like that solution. I was just confounded why it wasn't working for me.


you’re calling the old function


I found my problem!


Thanks for the help. 😀


Wow I was missing an end-brace in my one authentication handler


err it was misplaced in the map, the ending }


So yeah! don't pull out your hair if you have been working all day and you suddenly "break everything" you probably accidentally put an ending brace } someplace inopportune and you can find it with clear eyes in the morning.


Is it possible to do (swap! jpc-users-atom update-in [email :kanji-stumbles] conj kanji) and then simply do (swap! jpc-users-atom update-in [email :kanji-stumbles] disj kanji) to remove one?


Ignore swap. Does the day structure you’re updating allow conj and disj?


If it’s a set, then yes.


Swap just calls your function on the value of the atom. You can test everything outside of atoms and swapping and then know it will work in the context of atoms


can someone help me troubleshoot flycheck-clj-kondo? i've got flycheck installed (`M-x fly` brings up flycheck commands), I've got clj-kondo on my $PATH as evidenced by being able to fun it from the command line, but it's not doing any linting


M-x flycheck-verify-setup shows "no checkter to run in this buffer"


and running clj-kondo --lint my-source.clj returns 0 without seeming to have done anything


is there not a default configuration for clj-kondo?


@tstirrat what operating system are you on? what do you see when you do which clj-kondo and clj-kondo --version?


➜  ~ which clj-kondo 
➜  ~ clj-kondo --version
clj-kondo v2019.10.11-alpha


this is on ubuntu


can you maybe first upgrade to the newest version?


and how did you install this version?


The install script should work.


Can you upgrade and do clj-kondo --version once again?


installation script


i suppose i could try yarn/npm

Richard Desouza20:11:56

so i tried implementing this and have been trying to sort it from least to most

Richard Desouza20:11:57

(for [[p xs] (group-by :admin (cities))] [p (apply + (map :pop xs))]))

Richard Desouza20:11:09

but when i try applying sort-by in the manner i did before i get wrong number of args error

Richard Desouza20:11:10

(for [[p xs] (group-by :admin (cities))] [p (apply + (map :pop xs (sort-by :pop)))]))


sort-by requires a key-fn for comparison


you could take the result you have from the for loop and wrap that whole result in (sort-by > val ... )


basically it needs to know what you think matters for sorting

Richard Desouza20:11:46

dont know what u mean

Richard Desouza20:11:51

but i tried adding cities

Richard Desouza20:11:02

but its still largest to smallest


You have (sort-by :pop). There is no collection in that function call


And if you sort it and then map and sum everything the sort is unnecessary


right, the order of operations works from inside-out, so if you want it sorted you need to put sort-by on the outside

Richard Desouza20:11:46

(for [[p xs] (group-by :admin (cities))] ((sort-by :pop (cities)) [p (apply + (map :pop xs ))])))

Richard Desouza20:11:54

getting arrayseq cannot be cast to lang.ifn


is cities a (function) ?


what's the latest line of code you had that was working?

Richard Desouza20:11:30

(for [[p xs] (group-by :admin (cities))] [p (apply + (map :pop xs))]))


nice, it has a result you like, but it's not sorted correctly?

Richard Desouza20:11:21

its sorted from largest to smallest

Richard Desouza20:11:25

need smallest to largest


what does the result look like my friend?

Richard Desouza20:11:06

oh yea its not even sorted

Richard Desouza20:11:12

it just lists the provinces

Richard Desouza20:11:28

it adds all the hashmap province populations correctly

Richard Desouza20:11:38

just need to figure out where to put sort-buy


Excellent work so far


(sort-by second (for [[p xs] (group-by :admin (cities))] [p (apply + (map :pop xs))]))


Assuming you want to sort the pairs of province / population.


Say it out loud in words. “Once I compute the pairs of city name and counts, I need to sort by the population count”

Richard Desouza20:11:40

thanks sean that did it 🙂


Then it’s obvious the sort goes outside of the computation

Richard Desouza20:11:05

i see so like last operation that needs to be done goes on the outside


Right, because function arguments are evaluated before the function is called.

parens 4

(sort-by second (for ,,,))
;; evaluates (for ,,,) first
;; then calls `sort-by` on the result of that evaluation


And what does Second do? just grabs the second parameter of each thang?


Yes, because we have a sequence of pairs (province, population)


Second in this case is the population ?


(first pair) => province; (second pair) => total population (the sum of the population of each city within that province


So it's always a pipeline of operations, starting with the innermost and working outwards.


House.Price.Erase in clojure talk becomes (Erase (Price (House))) right?


Collection.Second.Sort-by (Sort-by (Second (Collection))) not a perfect mapping but maybe some good examples could help illustrate how functions bloom from inside out


I am not sure what language that is intended to be written in. If House.Price.Erase is written in Java syntax, and if the Clojure function price takes a value of type House and returns its price, and the Clojure function erase takes a price value and does something with it, then yes, (erase (price house)) would be how you write it.


However the verb erase you are using in your example makes me wonder whether it is intended to remove the field/property/attribute/whatever of the object house, in which case it is unlikely that you would write the Clojure code that way.


As Dan said above, say it out loud to get the order of operations -- and think about it as a single expression, rather than a sequence of imperative steps.


mm that's true, functional does not rely so much on sequential procedure


but in the abstract what do you want this transformation on this collection to look like

Richard Desouza20:11:37

ok so going back to a function i was doing previously

Richard Desouza20:11:51

actually nevermind

Richard Desouza20:11:55

i think im done 🙂

Richard Desouza20:11:03

thanks for the help guys


I owe much of my sanity to the fine chaps here.

Richard Desouza20:11:22

ill probably come back here when final exam season comes around 😛


And that function composition / call ordering is also why ->> might make it easier to think about:

(->> (for ,,,) ; compute total population for each province as pairs
     (sort-by second)) ; then sort by the second element of each pair


hey that's a great time to introduce thread-thru and double-thread-thru

Richard Desouza20:11:18

so that makes it not about order of operations?


think of a spool of thread


Is the class in clojure or just this particular homework?

Richard Desouza20:11:14

its just an assignment


->> thread-last takes the expression and puts it into the last slot of each "step" in order -- turning it into the nested function call you saw above.


clojure class would be Dope

Richard Desouza20:11:22

class is about a bunch of programming languages


Which other PLs are you studying @chrysalis129?


do you mind saying which institution?


(Understand if you don’t want to)

Richard Desouza20:11:13

were doing scala next

Richard Desouza20:11:22

and some other stuff

Richard Desouza20:11:28

but clojure and scala most indepth

Richard Desouza20:11:44

yea sorry bout that dps 😧


Well I’m glad that clojure is in curriculum somewhere :) good luck with your studies

Richard Desouza20:11:44

thanks dpsutton 🙂


Scala is... interesting... I did that for about 18 months before switching to Clojure. Do you already know Java @chrysalis129?

Richard Desouza20:11:37

know it a lot better than clojure thats for sure xd

Richard Desouza20:11:52

also know python C++ node.js

Richard Desouza20:11:10

trying to learn some other frameworks so i can get a software engineering internship soon

Richard Desouza22:11:54

hello again I have one more question

Richard Desouza22:11:13

say I have a one string that is "Hello" and a second string that is "Apple"

Richard Desouza22:11:42

is there anyway I can evaluate these two based on alphabetical order something like (> firstString secondString) evaluates to true?


Have you tried exactly that?

Richard Desouza22:11:25

java.base/java.lang.String cannot be cast to java.base/java.lang.Number


Try compare


And (doc compare)

Richard Desouza22:11:06

cant resolve doc


(clojure.repl/doc compare)


Not sure what your repl environment looks like sorry

Richard Desouza22:11:48

yea doesnt work either 😧


How are you running clojure?

Richard Desouza22:11:14

cmd with java -cp clojure-1.8.0.jar clojure.main filename.clj

Richard Desouza22:11:45

ok thanks i got it to work xd


@chrysalis129 you should consider running clojure with integration in your text editor - it’s a far smoother, richer experience


What platform are you on?


Hey all, in a philosophical moment I’m interested in a comparison or discussion of Clojure to other lisps/schemes/whatevers. Doesn’t need to be syntax focused, I’m as much interested in things like developer experience, “feeling”, purpose and historical accident results of various languages. Anything, basically. Any content anywhere to recommend? 🙂


One specific thing... what makes Clojure Clojure, as opposed to “almost Common Lisp” (or whatever). What subset of Clojure would be somehow enough to be thought of as Clojure, if at all possible or relevant?


I imagine you may have continued on other channels, but just to give my two cents — I would say the main differences that make Clojure pretty different from something like Common Lisp are that a) clj is much more functional, whereas CL is very multiparadigm; b) clj is hosted (on the JVM, or in the case of cljs, on JS environments); and c) clj uses a wider set of first-class collection data types (ie maps, vectors, sets, lists, as opposed to just lists).


(Yes, vague and fluffy. Any and all replies highly welcomed 😅)


@mattias504 That's probably a discussion better suited to #off-topic or perhaps #other-lisps rather than the #beginners channel (although I understand that beginners may be interested in this topic)

👍 4

I would seed that discussion with the material here and here (the latter is very short tho') -- most of the left-hand links in that "About" section are worth reading as background to your question and provide quite a bit of insight toward answers for it.


Thanks a lot! I’ll dive in and use other channels going forward!


There are also some more answers to rationale of differences between Clojure and Common Lisp or Scheme in a talk given by Rich Hickey about 2008 or so, transcript and link to video/audio here:


I'll keep an eye on both of those channels for any follow-up discussion -- it's a topic that interests me (and I have some opinions but not really in-depth knowledge of other lisps).