Fork me on GitHub

Hey so another quick question: for error handling, e.g sending wrong request parameters or invalid operations, is it ok to return nil all the way up until the service can treat it and respond the request with 4xx? Meaning the request handlers will look something like:

(defn get-something
  (let [value (get-function params)]
    (if value
and then get-function will probably be something like
(defn get-function
  (let [value (try-to-get params)]
    (if value
and that will probably continue... or is there a better way to do so?


I think this is reasonable, unless you want to give more specific error for different failures.


That second function could be

(defn get-function
  (when-let [value (try-to-get params)]


If you want some very simple, basic error handling/reporting, you might look at @hp_pedrors


Hmm I was thinking of returning an error code message, along with the value, so I can use globals and determine which message to send as well.


Anomalies supports error code (category) + message.


You could return {:ok value} or {:cognitect.anomalies/category ... }


Interesting! I'll take a look into it.


It's just data. You just decide on your conventions, write a spec, and off you go.

Cora (she/her)01:06:46

I have a vector that will be either 1 or 2 elements. is there a pattern-match-y way to do different behavior based on the length?

Cora (she/her)01:06:21

I suppose I could have a multi-arity function and apply them to it

Cora (she/her)01:06:46

it's a bit hacky though


Not really pattern-matching, but I’ll sometimes do:

(let [[a b] my-vec]
  (if (nil? b)
    (do-thing a)
    (do-other-thing a b)))

Cora (she/her)01:06:31

ok, that's basically what I'm doing but then the variable names kind of suck


in what way?

Cora (she/her)01:06:09

because if you named them anything meaningful then in either the single-value case or the double value case the variable names are wrong

Cora (she/her)01:06:23

unless you name them something long and not-so-great


cljs.user=> (case (count [1 2 3])
       #_=>   1 "one"
       #_=>   2 "two"
       #_=>   3 "three")


You can use apply to call you various arity functions

Cora (she/her)02:06:00

(defn- split->cnt+char
   [1 char])
  ([cnt char]
   [(parse-int cnt) char]))

(defn- encoded-string->cnt+chars
  (->> encoded-string
       (re-seq #"\d?\D")
       (map #(apply split->cnt+char (cstr/split %1 #"")))))

Cora (she/her)02:06:19

trying to run-length-decode here

Cora (she/her)02:06:34

so if a character isn't preceded by a number it should default to 1

Cora (she/her)02:06:45

> (encoded-string->cnt+chars "2B3CABR3T")
;; => ([2 "B"] [3 "C"] [1 "A"] [1 "B"] [1 "R"] [3 "T"])


(case (count my-vec)
  1 (apply some-fn my-vec)
  2 (apply some-fn my-vec))


Well if some-fn is overloading args, you don't even need case it'll be smart enough to dispatch on the argument count (edit, meant case, you still need apply)

Cora (she/her)02:06:22

I guess I'm just trying too hard to make clojure work like elixir's pattern matching


Destructuring is not pattern matching

Cora (she/her)02:06:14

yes, but I was trying to make it work like it was


I think Rich Hickey has an issue with pattern matching

Cora (she/her)02:06:41

I wanted to match on the number of arguments and do different things based on it


Because it complects two things

Cora (she/her)02:06:53

which I did using the multi-arity


So destructuring is for getting things out of a structure.


And after that, to perform conditional logic, you use conditional constructs

Cora (she/her)02:06:40

that's a strange argument

Cora (she/her)02:06:46

I don't get his meaning


^ @hp_pedrors That would be good for you to watch, based on our thread about error results.

thanks2 4
Cora (she/her)02:06:16

maybe not strange, just that I don't get it


You're coupled your flow to the structure


If you change your structure, you break all your conditional logic

Cora (she/her)02:06:47

I guess you don't need to know what exactly you have if you do (get structure key-or-index-or-whatever) but you do when you pattern match?

Cora (she/her)02:06:32

if I do (get-in m [:profile :address :zip-code] "no zip code!") I'm pretty sure I'm coupling to the structure here

Cora (she/her)02:06:57

and it seems a bit like an implementation detail, like your pattern matching could be built around accesses like that instead of perfectly matching structures

Cora (she/her)02:06:59

but what do I know


Ya, but not your conditional logic


Getting something out of a structure has to be coupled to the structure.

Cora (she/her)02:06:45

right, which I have to do anyways in order to do conditional logic


That is, unless it is a search, but like getting a thing in a precise position will always be coupled

Cora (she/her)02:06:35

sure, which is where I'm getting stuck on the difference. access is bound to structure already, especially when you're working with a very limited number of basic structures

Cora (she/her)02:06:51

and conditional logic that accesses will be bound to that structure

Cora (she/her)02:06:05

whether it's pattern matching or a get-in


What do you mean by conditional logic that accesses?

Cora (she/her)02:06:41

pattern matching is conditional logic that accesses values and uses that to determine the code path to take

Cora (she/her)02:06:23

whether I do that with a case or an if or pattern matching, I'm still accessing data to do the conditional and so my conditional logic is bound to the structure

Cora (she/her)02:06:40

doing away with pattern matching doesn't make it uncoupled

Cora (she/her)02:06:00

(get-in m [:profile :address :zip-code] "no zip code!") itself is conditional code that depends on a map with that kind of structure

Cora (she/her)02:06:30

find a value within nested maps this deep or else fall back to this other value


What do you mean? The condition would come after after, like (if "no zip code!" x y)


Also, I'm not sure about get-in, since you're right, it does a simple for of conditional with the default argument


But destructuring would not


It would just return nil or the value


Well, okay, it supports default as well 😛

Cora (she/her)02:06:58

I just don't get how pattern matching couples your logic to your data structure any more than normal accessing does

Cora (she/her)02:06:07

I'm not expressing myself well

Cora (she/her)02:06:04

sure, positional matching sucks because it's pretty tightly coupled but whether I do matching on [x y] or (if (= 2 (count arg)) I'm still coupled to the structure


Well, the first thing is that its a feature that complects binding and flow control. And Clojure likes to decomplect. So it chose to separate these two concerns.

Cora (she/her)02:06:02

I guess they can be two separate operations and that's the benefit?

Cora (she/her)02:06:15

I'm not sure I see the value, but maybe I will later


The second thing is that pattern matching as a flow control mechanism is normally closed for extension. So Clojure offers multi-methods instead to avoid that.

Cora (she/her)02:06:42

I don't know what that means, sorry

Cora (she/her)02:06:48

"normal closed for extension"


normally closed*

Cora (she/her)02:06:04

still not sure what you mean


Like, you can only extend it by modifying the pattern matching code itself


You can't from outside the function, for example say, oh and by the way, if the data matches to X do this other thing.


I mean, it is a pedantic argument

Cora (she/her)02:06:12

you'd need to define a multimethod and start using it at the place you wanted anyways if you wanted to extend it in the first place

Cora (she/her)02:06:44

it seems more like an argument that multimethods are good for extension not that pattern matching is bad in general


Well, in practice, nothing is always bad


But as a language feature, you have to think, I'm setting a norm, and is that norm likely to lead people to do things that I don't think is ideal most of the time?


Pattern matching has many libraries for it in Clojure. But Rich Hickey chose to make it more cumbersome to reach for, by not putting it in the core

Cora (she/her)02:06:36

it was in core at one point, looks like

Cora (she/her)02:06:38

but a while ago

Cora (she/her)02:06:16

but I get the point

Cora (she/her)02:06:21

I just disagree with it


To be honest, I'm not too sure myself, and now it has been a while since I've used pattern matching. Can you remind me in Elixir how you'd solve your above problem ?


> it was in core at one point, looks like Not sure what you mean there @corasaurus-hex?


That's a Contrib library. Not part of Clojure.

Cora (she/her)02:06:45

if not core, it was a core library?

Cora (she/her)02:06:05

@didibus you'd solve it much like the multi-arity thing I pasted above

Cora (she/her)02:06:19

iex(1)> my_vec1 = ["A"]
iex(2)> String.to_integer("3")
iex(3)> my_vec2 = ["3", "B"]
["3", "B"]
iex(4)> defmodule Foo do
...(4)>   def example(my_vec) do
...(4)>     case my_vec do
...(4)>       [char] -> [1, char]
...(4)>       [cnt, char] -> [String.to_integer(cnt), char]
...(4)>     end
...(4)>   end
...(4)> end
iex(5)> Foo.example(my_vec1)
[1, "A"]
iex(6)> Foo.example(my_vec2)
[3, "B"]

Cora (she/her)02:06:52

you could also do pattern matching in the function definition, but I just left it as the case statement


Okay, that's what I remembered

Cora (she/her)02:06:02

iex(7)> defmodule Foo do
...(7)>   def example([char]) do
...(7)>     [1, char]
...(7)>   end
...(7)>   def example([cnt, char]) do
...(7)>     [String.to_integer(cnt), char]
...(7)>   end
...(7)> end
iex(8)> Foo.example(my_vec1)
[1, "A"]
iex(9)> Foo.example(my_vec2)
[3, "B"]


Ya, I can only attribute it to wanting to encourage multi-methods over the use of case/switch


And even to some extent, protocols, though that won't apply to all pattern matching use cases

Cora (she/her)02:06:51

multimethods do seem good for extension, for sure


And I guess Spec now also does some form of pattern matching.

Cora (she/her)02:06:49

elixir does end up really brittle when you're passing around values from process to process and matching on data structures, but I can't see how it would be much different if I was using clojure and having to manipulate the datastructures from thread to thread

Cora (she/her)02:06:55

yeah, that's true

Cora (she/her)02:06:33

I'm gonna pop off for the night, but I've really enjoyed talking about all of this


Ya, I can't say for sure. I've never worked professionally in a language with pattern matching, only toyed with those. I can't say I ever "miss" it in Clojure, but it could be because I've never used it enough.


Have a good night


Pattern matching is really good for a closed set of options where the compiler can help figure out whether you've matched all cases. That's what you get in languages like Scala (and Haskell I think).


Given Elixir is dynamically typed, I don't see how it can get that support -- so I can see how it would become brittle without compiler support.


@corasaurus-hex For when you wake up... I also wonder if performance was a concern of Rich. You'll come to realize Rich Hickey is quite performance sensitive, and pattern matching has runtime overhead. So that could have been a strong reason to leave it out. Also... it was a choice for Clojure to be a Lisp with powerful extensions mechanisms. It is always okay in my opinion to reach for what you think is best, so if you really want pattern matching, a lot of people use core.match and defun ( as well in production and what not. So don't be shy. But maybe as a beginner, just to get accustomed, it's better to start practicing ways around it, and later, you can decide if you want it back and use a library.


My understanding in Elixir, and really in Erlang, is that it is actually unification


The = in Erlang is not assignment, but the unification operator


Which to be honest, I never understood the difference between unification and pattern matching 😛


Rich did investigate pattern matching early on. perf is one key concern (scaling with cases), and the other is the closed nature of the match set. polymorphic systems like multimethods and protocols are both open for external extension.

☝️ 4

Sweet, seems like I guessed right 😁


I also do think it complects binding and flow. For example:

(defun foo
  ([false true] 1)
  ([true false] 2)
  ([true true] 3)
  ([false false] 4))



(defn foo [[a b]]
  (case [a b]
    [false true] 1
    [true false] 2
    [true true] 3
    [false false] 4))


Much easier to refactor the destructuring fn in case the structure changes. Say it becomes a map?

(defn foo [{:keys [a b]}]
  (case [a b]
    [false true] 1
    [true false] 2
    [true true] 3
    [false false] 4))
(defun foo
  ({:a false :b true} 1)
  ({:a true :b false} 2)
  ({:a true :b true} 3)
  ({:a false :b false} 4))


As I see it, the whole point of destructuring (or one of its big use cases) is decoupling the fn body to the structure of the args. In this case, the body doesn't know how to get a and b from the arg, it's abstracted in the destructuring. But pattern marching reintroduces some of the body's logic back into it.


Does Intellij/Cursive provide auto complete for Java classes ? It doesn't work for me and I didn't find anything ? :face_with_rolling_eyes:


There's currently a bug in IntelliJ that's interfering with symbol resolution, maybe that's related? There's more discussion of the issue in the #cursive channel.


Thanks , I will ask there,


Are there any cases where tests would run properly from repl if i (run-tests


but break when i lein test? i get exception ctual: java.lang.IllegalArgumentException: Cannot open <nil> as a Reader.


differences in dependencies between dev and test


thanks! i noticed i am missing one


i use with-profile so i don’t have to repeat deps. like make a :shared profile and start the test with lein with-profile +shared test

Suni Masuno14:06:22

Style question, does the & go on the same line or last line in arguments?

  & b]


  [a &


I put all arguments on the same line. There should be only a few of them anyway.


(Exception: when you’re destructuring a map with a lot of keys.)


In long-lived code, I always destructure in a let inside the defn. I have trouble visually parsing fn args that destructure inline.


in joy of clojure 2 were shown this code:

(def very-lazy (-> (iterate #(do (print \.) (inc %)) 1)
                    rest rest rest))
;=> ..#'user/very-lazy

(def less-lazy (-> (iterate #(do (print \.) (inc %)) 1)
                    next next next))
;=> ...#'user/less-lazy
However this isn't what i get in my repl:
Clojure 1.10.0
(def very-lazy (-> (iterate #(do (print \.) (inc %)) 1)
                    rest rest rest))
(def less-lazy (-> (iterate #(do (print \.) (inc %)) 1)
                    next next next))
has next changed?


iterate has changed, in 1.7.0, I think?


i suppose i'm having a hard time demonstrating that rest is more lazy then next.

(next '(1))
=> nil
(rest '(1))
=> ()
Doesn't, to me, demonstrate laziness, there are just different return values. The claim is that rest doesn't force realization of remaining elements, but i can't build an example to demonstrate this.


I think it will depend on the seq you're using


if you're digging into this area, this (historical) page from around the time of the seq overhaul may be of interest


() vs. nil does actually demonstrate it, but you need have more context, basically nil in this case means there for sure no more elements which means rest has inspected the contents (which is not a lazy operation). next returning () means there may or may not be more elements and next has not inspected the contents


Thanks Alex! I'll take a look.


"rest has inspected the elements" did you mean next here?


sorry, yes, swap them around


I never bothered remembering which is which, I use rest everywhere and call seq any place I want to nil pun


(rest predates next and used to behave more like next)


back in the day there was no "empty seq", just nil, and () was an empty list


ah, ok. this example makes sense to me: In order to demonstrate the difference you need to make sure some other factor isn't going to eval the body and trigger the side effect (that helps show the lazyness). Here def does that, sense rest-test points at the un examined body as where next-test prints when called with def because it evals the body.


i suppose its simpler to use rest everywhere except when your specifically going to use the nil return value to check an exit condition? Joy of Clojure seems to suggest the opposite though 🙂 > In general, we recommend that you use next unless you’re specifically trying to write code to be as lazy as possible.


I tend not to use either of them most of the time


instead, think in manipulating colls and seqs most of the time


in case of loop/recur, lean on destructuring


there can be some very subtle implications if working with walking nth items down via next/rest but you really can ignore that 99.9% of the time


far enough. I think i understand them enough now to be aware of when the difference will matter. As you said, i mostly try to work with collections. Thanks again alex!

Cora (she/her)18:06:44

@alexmiller @didibus I guess I don't see why pattern matching is even being compared to multimethods and protocols, and why they're mutually exclusive. if I wanted to allow external extension I'd do multimethods and protocols (which elixir has as well), but most times I just want to do different things based on the structure I have (as in my original question) and the structures are used only within a function or namespace


Are you sure Elixir has multi-methods? I thought it only had Protocols?

Cora (she/her)19:06:14

I meant to only indicate protocols


Anyways, when you need a quick off the hand conditional restricted to a single function, normal conditional constructs are just fine. The danger is if you're using that same conditional logic over and over in different places, and if you are constantly having to extend it with new cases. That's when multi-methods will shine and be a better tool in my opinion


In your case. It seems what you're interested in is some kind of duck typing conditional. Like, what kind of structure do I have here. And based on that you would branch out.


Multi-methods are great for that. But they assume this will be used a lot. Like if your structures were top level entities of your app. It's overkill for you I agree


With your example, I think it's hard to understand. But in simple cases like yours, there are alternatives like you've seen. The fn overload is one, if you just want to branch on number of elements, it will also perform a lot faster.

Cora (she/her)19:06:49

yeah, multimethods do feel like a lot of ceremony for the small case

Cora (she/her)19:06:06

I can see how having all the matches everywhere could lead to harder-to-change code


I guess to see the difference, we'd need to have an example of pattern matching and multi-methods applied more to solve an architectural design issue.

Cora (she/her)19:06:04

pattern matching comes off so elegant, to me, it's almost grating not to be able to use it, but thinking back to my elixir apps I do end up having to duplicate a lot of structures


I do think pattern marching can make some things simpler and clean and easy to read. I think the reason not to include it in the core was the fear that it would also quickly be used for larger things which multi-methods or protocols are better suited for.


So, another alternative, which I use, when your conditional logic itself is determined by the structure of data, is to use Spec


You can spec the various options using the or spec. And then use conform to know which one it conforms too in order.


The name of the first spec to match will be returned.


It is still more verbose then pattern matching though


Like this:

   :char (s/tuple any?)
   :char-count (s/tuple any? any?))
  [1 "c"]) 


That will return :char-count

Cora (she/her)19:06:22

ahhh interesting


If you pass it ["c"] it will return :char


Now you can imagine having a multi-method whose dispatch function is the above conform code.


Maybe you call it: decode


Still overkill in your case. But for a more complicated case it could be quite nice

Cora (she/her)19:06:10

or just (if (= 1 (count my-vec)) [1 (my-vec 0)] [(parse-int (my-vec 0)) (my-vec 1)])

Cora (she/her)19:06:40

so many choices

Cora (she/her)19:06:05

I do like the function arity dispatch thing I did


I mean in your case, I think function overloading was pretty nice

Cora (she/her)19:06:35

I think I'm starting to see why they're being compared now


After that, a conditional on count is probably second best, like with if or case if you have more then 2 branches

Cora (she/her)19:06:03

yeah, case with (count my-vec)


Ya. So I'm not gonna lie. Pattern matching in your situation also seem quite nice. I think for a lot of small simple algorithms, especially with recursive conditional branches, pattern matching is really good. And that's probably its sweet spot


And if you're doing a lot of these (and aren't trying to learn Clojure but know it well already 😉), it could make total sense to reach for core.match and defun in that scenario


Well, unless you cared about performance

Cora (she/her)20:06:04

I'm coming from ruby, performance is relative 😜

Cora (she/her)18:06:01

do people reach for multimethods and protocols in the case I shared last night?

Cora (she/her)18:06:25

(defn- split->cnt+char
   [1 char])
  ([cnt char]
   [(parse-int cnt) char]))

(defn- encoded-string->cnt+chars
  (->> encoded-string
       (re-seq #"\d?\D")
       (map #(apply split->cnt+char (cstr/split %1 #"")))))

Cora (she/her)18:06:44

I just wanted to do different things based on whether the vector was two elements or one element


I feel like I don't commonly encounter this situation

Cora (she/her)18:06:52

that's fair, it might be a weird situation

Cora (she/her)18:06:59

run length decoding is an odd one


instead of applying, you could just destructure

Cora (she/her)18:06:01

I had something like that, but then the variable names get a little weird. like if I mapped them I'd have [cnt char] but if char is nil then cnt is actually the char


yeah, usually the more optional part is on the right so it's not weird

Cora (she/her)18:06:55

I could reverse them first? ha

Cora (she/her)18:06:11

I see some pattern matching macros for clojure but I'm going to try not to make clojure into erlang/elixir and just try to solve problems clojure-y ways


@corasaurus-hex is there anything stopping you from having the function return char+cnt instead of cnt+char?


I think that would solve this problem from a syntax POV

Cora (she/her)18:06:42

the encoding itself is cnt+char

Cora (she/her)18:06:59

I'm just going through exercism with clojure and I hit the run length encoding exercise

Cora (she/her)18:06:13


Cora (she/her)18:06:08

so when you split up "2AB3CD4E" with that regex you end up with something like [["2" "A"] ["B"] ["3" "C"] ["D"] ["4" "E"]]

Cora (she/her)18:06:01

and so I'm going through and left-padding the single element vectors with 1 and then parsing the first elements from the two element vectors to integers

Cora (she/her)18:06:42

then I can run through and repeat and concat

Cora (she/her)18:06:53

and then go back and combine steps where it makes sense

Cora (she/her)19:06:49

so I could make it char+cnt but I'd have to reverse each inner vector or do some regex magic or something


yeah. I think what you’re doing now seems best 😄

Cora (she/her)19:06:39

I'm probably fixating on too minute a problem but, unfortunately, this is how I learn ¯\(ツ)

Cora (she/her)19:06:36

I end up learning stuff thoroughly, though, so that's nice


you’ve uncovered a small shortcoming in Clojure’s default ability to express case matching. it doesn’t have as good of support for positional matching as, like you said as an example, Elixir


that’s good knowledge!


if you’re doing something that involves a lot of positional matching (like writing a parser/lexer) then you now know that you’re going to need some extra batteries if you decided to do it in Clojure

Cora (she/her)19:06:03

for sure, I would expect that now

Cora (she/her)19:06:26

and next time I come across the issue in the small case I know not to fixate on better ways to solve it, it's just kind of less than ideal

Cora (she/her)19:06:42

every language has that stuff, just move on 🙂


yes 😄 hopefully you also get to use some of Clojure’s stronger features like it’s rich polymorphic dispatch via multimethods and protocols


they work a lot better when combined with more descriptive data like maps and types/records

Cora (she/her)19:06:11

yeah, for sure. I've learned those but I haven't yet come across a use case where I knew I wanted to use them. the problems I'm working on are pretty small though


yeah. I find in applications, I tend to reach for multimethods. When writing a library, I tend to reach for protocols


Hey, can you suggest a Clojure repo that can be considered as "idiomatic"/well written? I know 101 level clojure and I want to study a repo to see if I'm actually going to like the language

☝️ 4

many people recommend weavejester's as great example code


Is there any type of problem/task to solve that helps appreciating clojures stronger sides compared to other generic langs?(idk if this is good question tho)


Clojure runs on the JVM, so it has the same pros and cons as the JVM. As a language, it has the same abilities as any other mainstream language. I would say the two of the stronger selling points are 1. code is data : which gives you the ability to pass functions as arguments and return them. 2. immutability as default: which means that hard to reason about state transitions become the exception and not the norm. I migrated from python -> ruby -> clojure and it's clear to me that everything i found useful in those languages has its roots in LISP and everything else ...


"code is data" is something else. First class functions/higher order functions is what gives you the ability to pass functions as arguments and return them.


"code is data" is what is behind macros: the ability to operate on Clojure code as a data structure -- transforming code prior to evaluation.


Yep, I've heard those a lot but havent had much mind-blowing moments tho.


Lots of languages provide 1st class fns and higher-order fns these days -- you can even get most of that in Java now 🙂


Code is data implies macros and ability to manipulute directly eval tree I guess


And macros are not anywhere near as "popular" in production Clojure code as many people would seem to think, when they first start learning about macros 🙂


For example, from our code base at work

Clojure source 279 files 66902 total loc,
    3118 fns, 641 of which are private,
    422 vars, 29 macros, 60 atoms,
    580 specs, 21 function specs.


(that's not including 20k lines of test code, but we don't track macros in that)


Given that Clojure is, by design, a "general purpose language", I think it's hard to point to a specific type of problem/task that shows Clojure's strengths.


It's great at data manipulation/transformation. It's great at concurrency (in various forms). Perhaps the real "killer aspect" of Clojure is a good REPL-driven development workflow? It's incredibly productive because of that extremely tight feedback loop.


Maybe its strong side is, since LISPs can look messy easily it forces me to write more fns, break-down the problem. Which is usually a good thing


REPL development (both using a actual repl or with immediate feedback using fighweel) is a big part of the "fun" here. It is hard to describe using toy examples since toy examples are usually read (not written) by those "outside" the clojure community.


I'd say Clojure excels at big boring enterprise apps


Just like Java does 😋


Except Clojure makes working on those fun again


So ETL processes, data pipelines, micro-services, SOA, etc.


Clojure is best when your state is inside a data store like S3, Mongo, MySQL, a file on disk. etc.


And your logic is mostly getting data, transforming it, putting it back, dispatching events to some other services, rinse and repeat


That makes it a really good choice as a backend for SPA apps as well


Which is where ClojureScript comes in handy, cause it excels at front end SPA


Clojure isn't so great for short lived programs, like grep, or other one shot quick command line tools


Or for high performance low level code, like graphics engine, audio processing, AAA games, etc.


Which is kind of unfortunate, because Clojure is better for big applications, but for small weekend projects, which is what people often play with first, it doesn't really shine. Though its definitely still good. But you won't really understand the benefits in those scenarios


Which all puts me in mind of my Clojure/West talk from many years ago "Doing Boring Things with an Exciting Language" 🙂


Haha, definitely, when the problem is boring, make your tools more exciting


It's also why I feel it's good to complement Clojure with one or two more languages, if you want to cover all possible use cases.


A lot of people complement it with Rust for low level high performance things.


I think to some extent OCaml can be used for this as well, or Go.


But these will also hit a limit. So I feel Rust really covers the other half of Clojure. Or Nim, C, C++ as well.


For scripts that don't need high performance, you have ClojureScript or Joker which work very well, and are easy to pick up as they are mostly Clojure. You can even use Clojure most of the time. Like, if you don't need uttermost performance, you can generally also accept the slower start time of Clojure


Almost everything I do is server-side, long-running processes, so "all Clojure" is fine 🙂


The last bit I think is not covered is Android and iOS development. ClojureScript works for that though


But I try to follow Pragmatic Programmer advice of learning a new language every year or so... Last year was Kotlin (quite nice). Go was several years ago (disappointed). Rust was in between (liked it, but I don't do any low-level stuff these days).


Haha, ya me 2


Elm was another fun language to learn (several years ago).


But I like the thought that I've got all cases covered.


My next ones that I'm interested in strongly are Rust, Rebol, OCaml


Have you ever heard of Fantom ?


Rust is fascinating. I haven't looked at Rebol. I did look at OCaml years ago (but ended up learning F# instead!).


I actually really like that language. But it is so unpopular


Rebol looks really interesting from a meta programming level


I think it might win as the next language I want to try


Heard of Fantom. Never looked at it.


It's basically taking DSLs to the extreme


Oh Fantom. Ya


Its like what I'd want Java to be


It's like how I find C# to be a better attempt at Java, and then I find Fantom to be a better attempt at C# :p


Though... like Go, it only has generics for bundled data structures


Have you tried Kotlin? That does a good job of being a "better Java".


I never tried it no. But I've had to browse Kotlin source to debug things before


But my first impressions weren't that great to be honest


I feel a little similar to how I feel about Scala. It tries and carve that weird middle of almost FP, but not really, almost hindley Milner, but not really


And I'm not sure I like it


I did Scala for about 18 months for work. I liked it at first and gradually grew to like it less and less, the more I worked with it. 🙂


Hello. I'm wondering if someone can point me in the right direction to correct this log format issue. I'm using timbre for logging on Windows, and when I log an exception with timbre, I see all these characters that I think are ascii encoding or something? Is there something I can use to make the printing look as it should?


My first line of attack would be to see if you can get a terminal on windows that supports ansi colour codes, but that's outside my knowledge - if you can't do that, you can turn off timbre colour codes for stacktraces. see


what are you running your repl in?


Ah thanks. Tried powershell and cmd


do you have git bash laying around?


you can turn off aviso.pretty, that's what colorizes


Ah I see. I may take that route. I do have git bash. I can try that too


Update: I found on stackoverflow that: >in latest Windows 10, you can enable ANSI in conhost via the following reghack -- in HKCU\Console create a DWORD named VirtualTerminalLevel and set it to 0x1; then restart cmd.exe.


This worked for me 🎉


@brandon.ringe Alternatively, maybe check the TERM value (or any cmd equivalent) to see why it thinks your term supported ANSI when it apparently didn’t. Otherwise your logs might get littered with ANSI escapes too.

👍 4