Fork me on GitHub
#beginners
<
2020-10-30
>
stopa02:10:16

Hey team, for fun, I was playing with an “emoji-based” programming language

stopa02:10:07

I wrote up:

(read-string "(➕ 1️⃣1️⃣ 1️⃣)")
But this ends up failing with:
Invalid number: 1️⃣1️⃣
I am guessing Clojure doesn’t know how to interpreter this emoji character. Do you have some ideas on how I could get around this?

seancorfield02:10:49

You'll have to write your own reader to interpret those emoji as digits.

👍 1
stopa03:10:09

Makes sense, will play with it! (To make sure, there is no special “reader” changes I can do inside Clojure right? I am guessing what I would do is just write my own parser for this string)

seancorfield03:10:26

Well, you need to interpret characters differently to Clojure's reader.

seancorfield03:10:45

You are assigning very different meanings to characters.

👍 1
stopa03:10:02

makes salute indeed, makes sense : }

seancorfield03:10:21

Emojis are otherwise just "regular characters":

user=> (def :keycap_ten: 10)
#'user/:keycap_ten:
user=> (* 123 :keycap_ten:)
1230
user=>

❤️ 1
seancorfield03:10:52

Oh, that didn't work... :keycap_ten: was the 🔟 emoji!

❤️ 1
stopa03:10:02

Veery interesting. I guess the “first” unicode codepoint of 🔟 is not a number, so Clojure is able to handle it

stopa03:10:43

one more question: Given a String like this: “1️⃣1️⃣” How would one go about “splitting” them by emoji? I am quickly discovering that 1️⃣ is actually three unicode codepoints. Is there some recomended utility function that figures out the boundaries for emojis?

seancorfield03:10:00

Are you on Windows, Mac, Linux?

seancorfield03:10:06

1️⃣ should be a single unicode thing, but if your encoding is off you'll get weird results.

stopa03:10:25

(def keycap-one "1️⃣")
=> #'emote/keycap-one
(.codePointAt keycap-one 0)
=> 49
(.codePointAt keycap-one 1)
=> 65039
(.codePointAt keycap-one 2)
=> 8419
^ This is what I am seeing on my laptop (Mac) From some research, am finding out that this may have something to do with “unicode scalars” https://stackoverflow.com/questions/30757193/find-out-if-character-in-string-is-emoji

seancorfield03:10:58

I suppose that makes sense, given

user=> (count (name ':keycap_ten:))
2

seancorfield03:10:12

Oh, darn Slack's "help" here!

😆 1
stopa03:10:45

(count "👨‍👨‍👦‍👦")
=> 11
😅 one day i need to build my own string class or something, to finally understand unicode idiosyncrasies once and for all

seancorfield03:10:59

@stopachka Trying using MySQL... and really discover that "unicode" has multiple meanings!

😮 1
stopa03:10:15

Update on this: Java’s ICU4J seems to do the trick! They “BreakIterator” can understand these weird unicodes

hiredman05:10:01

I think something like (partition-by #(or (Character/isHighSurrogate %) (Character/isLowSurrogate %)) some strong) might do it

👍 1
hiredman05:10:08

Unicode "scalar" is some swift specific jargon

hiredman05:10:23

Java strings are utf16 which splits certain characters into multiple code points, which, annoyingly is sort of really what java characters are

hiredman05:10:04

Each java char is a utf16 codepoint, which may or not be a complete character

hiredman05:10:08

So like if you count the characters in a string of emojis (or other characters outside the bmp) you get many characters for every emoji

hiredman05:10:19

You can get the string encoded as utf8 bytes, at which point you don't have to worry about codepoints, however now you have to worry about characters being multiple bytes instead of multiple codepoints

Jim Newton09:10:40

is there a way in clojure to redirect stdout to a function? Something like the following:

(binding [*out* (make-function-stream f)]
  (call-some-client-code))
then anytime the client code tires to print something to *out* rather than it appearing in the output, f will be called with that string as argument. is this possible?

Jim Newton09:10:48

yes I know about with-out-str, but that's not what I want. with-out-str returns a string of everything that has been printed to stdout while evaluating its body. That's different than what I'm asking for.

Jim Newton09:10:58

I want to call a function on the output, but not effect the return value of the expression

Jim Newton09:10:19

the binding form above returns the value returned by call-some-client-code

Jim Newton09:10:22

I mainly want this for debugging to watch for something being printed and either supress it or emphasize it

delaguardo09:10:03

I guess your function should return something that extends this class https://docs.oracle.com/javase/7/docs/api/java/io/Writer.html

Jim Newton09:10:32

hmmm. that looks awfully complicated, and probably can't be done directly from clojure. Is there a way in clojure to create a subclass of a java class and override a set of methods with clojure functions?

delaguardo09:10:57

no it is not complicated and totaly doable in clojure maybe next time instead of calling something “awfully complicated” you will seek and ask for extending your domain expertise

Jim Newton09:10:39

sorry, I should say, "That java documentation looks pretty complicated" especially to someone with no java experience. @U04V4KLKC, if you'll notice, my next sentence after calling it complicated was exactly asking how to do it in closure. Sorry, if my comment sounded crass. I appreciate your offer to "extend my domain expertise".

delaguardo09:10:28

then look at proxy it is a little bit complicated and requires some basic java but I think this is the fastest way to achieve what you want

Jim Newton09:10:05

I did an approximation, when suffices for supressing stdout, but doesn't have my original asynchronous behavior, proxy looks interesting indeed.

(defn capture-output
  "Call the given 0-ary function, returning a vector of length 2.
   The first element is the value returned from thunk.
   The second element is the string comprising all the output
   printed to *out*, or \"\" if it printed nothing."
  [thunk]
  (let [a (atom nil)]
    (reverse [(with-out-str (reset! a (thunk)))
              @a])))

(defn call-diverting-stdout
  "call the given 0-ary thunk, returning its return value.
  If anything is printed to *out* during the dynamic extent
  of thunk, it will be captured into a string.  diversion-f
  will be called on that string (once).  If nothing was printed
  then diversion-f will be called with empty string."
  [thunk diversion-f]
  (let [[value str] (capture-output thunk)]
    (diversion-f str)
    value))
    

Jim Newton10:10:18

BTW is there a literal string syntax which allows me to avoid backslashing quotation marks within a string? something like """this is a string with "''s in it"""

delaguardo10:10:44

no, in clojure there is only " as a string literal

Andrei Stan09:10:12

Hello, i am learning to use clojure.spec.alpha http://clojure.specs.to #:clojure.spec.alpha :

(spec/def ::name string?)
(spec/explain-data ::name 0)
;;=>
;;#:clojure.spec.alpha{:problems [{:path [],
;;                                 :pred clojure.core/string?,
;;                                 :val 0,
;;                                 :via [:ns/name],
;;                                 :in []}],
;;                     :spec :ns.ns/name,
;;                     :value 0
• when i try : (:problems (spec/explain-data ::name 0)) it returns nil

Andrei Stan09:10:06

I will close this one, as i found that i must access the keyname preceded by the domain :clojure.spec.alpha/problems`

Jim Newton09:10:45

short of that How do I bind to /dev/null ? I tried the following the advise on https://stackoverflow.com/questions/7756909/in-clojure-1-3-how-to-read-and-write-a-file

(require '[])
(binding [*out* (writer "/dev/null")]
   (try (not (disjoint? a b))
        (finally (close *out*))))
but it tells me the writer function cannot be found.

delaguardo09:10:36

either replace (require … with (use … or (better option) use alias for http://clojure.java.io namespace (require '[ :as io]) (io/writer "/dev/null")

mbjarland09:10:30

I'm back with another xml zipper question. How would I match for xml nodes which do not have a specific child node? So assume I have this:

(zx/xml1-> xml
           :catalog
           :book
           [:author (zx/attr= :gender "female")]
           [:rating]
           zip/node)
this would give me all nodes with a rating, but assume I wanted to reverse/complement the [:rating] subquery/predicate, how would I go about doing that? (`[clojure.zip :as zip]` and [clojure.data.zip.xml :as zx] and xml is a zipper over xml) Tried something like this but I think I'm not grokking zippers fully yet as it doesn't work:
(zx/xml1-> xml
           :catalog
           :book
           [:author (zx/attr= :gender "female")]
           [(complement (zx/tag= :rating))] ;; umm I think I understand why this doesn't work now, still not how to do this though
           zip/node)

Jim Newton09:10:25

I've searched for a bug for a few hours, and it turns out to be the same genre of bug I've made before. I was convinced the error was in complicated logic elsewhere in the program, but it turns out it was very simple. I had something like the following.

(let [i (compute-something my-seq)]
  (cond (= i)
        (compute-something (reverse my-seq))

        :else 
        i))
What I meant to say was (= i some-value) not (= i). Of course i is always equal to itself, (perhaps NaN being a counter example???).

lassemaatta10:10:31

I suggest using clj-kondo, which seems to detect that issue: Single operand use of clojure.core/= is always true

Jim Newton10:10:53

at some point in the past I installed clj-kondo. is it supposed to work automatically, or do I need to explicitly run something on my code every time I press C-c C-c ?

Jim Newton10:10:35

looks like I had it installed, but removed it. Don't remember why now...

lassemaatta10:10:46

I don't use emacs, but the editor installation guide refers to flycheck-clj-kondo, which uses flycheck , which appears to be a "on-the-fly" syntax checker. So I assume it should work automatically in emacs if it's installed correctly.

delaguardo10:10:16

there is how = defined in clojure.core

(defn =
  "Equality. Returns true if x equals y, false if not. Same as
  Java x.equals(y) except it also works for nil, and compares
  numbers and collections in a type-independent manner.  Clojure's immutable data
  structures define equals() (and thus =) as a value, not an identity,
  comparison."
  {:inline (fn [x y] `(. clojure.lang.Util equiv ~x ~y))
   :inline-arities #{2}
   :added "1.0"}
  ([x] true)
  ([x y] (clojure.lang.Util/equiv x y))
  ([x y & more]
   (if (clojure.lang.Util/equiv x y)
     (if (next more)
       (recur y (first more) (next more))
       (clojure.lang.Util/equiv y (first more)))
     false)))
notice one-arity version which always return true

delaguardo10:10:13

and clj-kondo should not give you any warnings about using single arity variant of = because this is a valid usage per ce

Jim Newton10:10:02

yes, that makes sense. I see that correctly (= ##NaN ##NaN) returns false

Jim Newton10:10:16

@UC506CB5W yes seems to work automatically when I enable flycheck-mode in emacs.

Jim Newton10:10:21

it also caught the mispaced docstring which is an error I very often make.

delaguardo10:10:06

cool, didn’t know about such warning, maybe it is a time to upgrade my clj-kondo)

mbjarland10:10:23

ok replying to myself here. Wrote the following (by rewriting tag= )

(defn without-tag=
  "Returns a query predicate that matches a node when its is a tag
  with name different from tagname."
  [tagname]
  (fn [loc]
    (and (not= tagname (:tag (zip/node loc)))
         (empty? (filter #(and (zip/branch? %) (= tagname (:tag (zip/node %))))
                        (zf/children-auto loc))))))
after which you can do:
(zx/xml-> xml
          :catalog
          :book
          [:author (zx/attr= :gender "female")]
          [(without-tag= :rating)]
          zip/node)
still leaves me feeling like there should have been a more direct way than rewriting one of the clojure.data.zip.xml functions, but this works

Jim Newton11:10:37

maybe Python would be a better choice as most people (from non-lisp background) find the syntax very intuitive.

Ben Sless13:10:58

Maybe something along Automate The Boring Stuff With Python? Clojure is versatile enough (And has Java to draw on) to be able to port most examples from there.

Jim Newton11:10:47

adivce welcome..

Louis Kottmann11:10:06

ruby is good, it is meant to be close to spoken language

Louis Kottmann11:10:00

clojure requires more mind bending, for a midly interested novice I wouldn't say it's good starting option

Jim Newton11:10:18

in 1990 lisp was an excellent language to start with using SICP. it was straightforward, and synthetic.

Jim Newton11:10:51

I know neither ruby nor java script. but JS seems to the most popular language at the moment.

Louis Kottmann12:10:32

JS is so bad a language it was remade in pretty much every other languages

Louis Kottmann12:10:40

I wouldn't recommend that either ^^

Jim Newton11:10:35

an interesting feature of the Mark Lewis (Scala) book is that it doesn't touch intelliJ, it just using any editor to create files, and then the unix command line to exec the programs.

Jim Newton11:10:14

our students use OCaml to learn programming. 🙂

Jim Newton11:10:30

they usually hate it. except a few who understand

Jim Newton11:10:14

@lkottmann nice avatar pic, BTW. is that you? who did the art work?

Louis Kottmann11:10:23

yes it is me, my artistic director made it 🙂

Jim Newton11:10:48

👍:skin-tone-2:

Louis Kottmann11:10:51

she makes one for every employee as well, with a different background based on the position

Jim Newton12:10:28

I don't know your personality, Louis, but I imagine something seeing the smirk on the face of the avatar 🙂

Louis Kottmann12:10:34

yeah she nailed it

Louis Kottmann12:10:24

it's harder to tell on a smurf!

Jim Newton12:10:32

it's a smurf playing an oboe

Jim Newton12:10:00

you think you're friend will do an image of me if I send her a picture or a video of me?

Louis Kottmann17:10:08

why not, a picture will do

delaguardo11:10:34

There is another channels for discussions like that ) #other-languages or #off-topic

Louis Kottmann12:10:08

that being said, and to stay on-topic, clojure is very well designed if he can punch through the first hurdles, it can be a good starting language as well

Louis Kottmann12:10:01

no systemic pitfalls, no 10-ways to do one thing because of poor evolution, and the syntax is homogeneous

manutter5112:10:03

@jimka.issy Is there something in the Racket multiverse that would be a good fit? I do think Clojure itself is a good fit, and I’ve had some thoughts along the lines of “Wouldn’t math be easier to learn if they used Clojure to teach it?”

1
Jim Newton12:10:23

being a mathy, it is hard to think like a non-mathy. But I teach a course in introduction to Scala programming. I teach them about higher order functions by implementing the calculus. I first implement limit, which takes a function, and returns a function which computes the limit at the given point. Then given limit, we write derivative and integral. Then given integral and derivative we write double-integral and gradient.

Jim Newton12:10:44

I got some complaints at the end of the semester, that this is a programming class, not a math class 😞

Jim Newton12:10:52

for me it was a natural thing to do.

Jim Newton12:10:18

functions that take functions and return functions. derivate and integral are such functions they've already seen many times in their life before this class.

manutter5113:10:05

I saw a talk at Lambda Jam (I think it was) and one of the SICP guys basically went through higher math functions from calculus through relativity using LISP notation instead of the arcane glyphs of classical maths, and it was astonishing to me how much clearer and more legible it all was. I was following most of it up until relativity when my brains said, “Oh, I couldn’t eat one more bite, honestly.” I’ve never had calculus, but I would love to take a Clojure-based course in it.

❤️ 1
manutter5114:10:05

I think there’s a strong correlation between “mathiness” and elegance in coding, just because of the nature of the domain.

andy.fingerhut15:10:08

But for a bunch of people who get into programming, their ideas of applications that motivate them often have little or no relation to calculus

manutter5117:10:27

Little or no apparent relation to calculus 😄

manutter5117:10:33

Seriously, though, I was just talking the other day with an internet friend about the relationship between math and programming, particularly with respect to recursive algorithms

manutter5117:10:33

I’m trying to remember what math class it was, geometry I think, where we spent a lot of time trying to work out inductive proofs of various theorems, and it seems to me that a lot of the same thought processes go into a lot of programming tasks.

stopa18:10:20

> saw a talk at Lambda Jam (I think it was) and one of the SICP guys basically went through higher math functions from calculus through relativity using LISP notation wow that sounds aweesome!

stopa15:10:13

Also, tangent note of thank you: @andy.fingerhut, I keep noticing your comments on clojure docs. Thank for your work! (Found ICU4J from your capitalize comment)

andy.fingerhut15:10:30

You are welcome. I often try to leave examples there, often text in comments rather than code, that is useful to people looking for how to do things

❤️ 2
parker16:10:58

FWIW, I was in the same place at that age, and picking up Clojure was what made programming really “click” for me. Not having to muck with mutation and side-effects (except where you really want that) was game-changing.

parker16:10:42

The downside was that most of the documentation and learning resources for Clojure are strongly geared towards people who already program. I don’t know if that’s worse than the aggressively surface-level js/python “tutorials” that have proliferated, but it’s a thing to keep in mind

parker16:10:50

playing around in quil might be a good place to start!

parker16:10:00

Elm could be a good option too. I don’t think that kind of type system is bad for someone new, having the checker there to make sure all the legos fit together is more helpful than confusing

practicalli-john18:10:39

Without using re-matches (as I have a solution using that approach), is there a function or simple function composition to detect if a sting has contains only case characters, or is only upper case characters. The string may contain non-alphabetical characters e.g. "I am SHOUTING!" should be false, "REALLY * SHOUTING :)" should be true Just wondering if there are alternatives to re-matchers

seancorfield18:10:52

@U05254DQM I'd probably use re-find to look for any lowercase chars and then just not the result.

practicalli-john18:10:18

ah re-find, that sound more sensible than using re-seq and counting the difference in characters... I'll have a play with both 🙂

seancorfield18:10:10

You'll also need to consider whether you just want to exclude [a-z] or whether you want to exclude all unicode lowercase characters 🙂

seancorfield18:10:59

#"\p{Ll}" will match any lowercase unicode character (to save you looking it up).

practicalli-john19:10:20

(not (re-find #"[a-z]" phrase)) to start with. #"\p{Ll}" is new to me, looks fun, thanks.

practicalli-john19:10:54

I did a re-matcher expression as well, (re-matches #"[^a-z]*[A-Z]+[^a-z]*" phrase)

Gleb Posobin02:10:44

(= phrase (string/upper-case phrase))?

practicalli-john08:10:52

I started with that then found the edge case of a string with no alphabetical characters, e.g "1 2 3". That isn't shouting, but is equal to it's the upper-case version of itself.

Gleb Posobin18:10:55

(and (= phrase (string/upper-case phrase)) (not= phrase (string/lower-case phrase)) then?

practicalli-john18:10:35

Yes, this is the approach I took, except I decided to re-learn regular expressions again. I use re-matches with a regular expression to check for lower-case characters before and after any number of uppercase characters: (re-matches #"[^a-z]*[A-Z]+[^a-z]*" phrase) I found this cheatsheet really helpful https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet

MatthewLisp18:10:32

Hello 👋 What's an idiomatic way of removing leading zeros from a string? "00012030" -> "12030" I've done (defn remove-leading-zeros [s] (.toString (Integer/parseInt s))) But seems awkward, plus if i use a input that's a non-digits string like "hello" or nil value, this function I've written will throw

seancorfield18:10:47

(clojure.string/replace s #"^0*" "") ?

seancorfield18:10:26

To accept nil you'd need (some-> s (clojure.string/replace #"^0*" ""))

👍 1
MatthewLisp18:10:59

funny that it didn't crossed my mind to use regex 😂