This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-09-05
Channels
- # asami (13)
- # aws (7)
- # babashka (4)
- # beginners (16)
- # biff (7)
- # cljdoc (10)
- # clojure (32)
- # clojure-europe (27)
- # clojure-nl (14)
- # clojure-norway (7)
- # clojure-uk (3)
- # clojurescript (2)
- # conjure (2)
- # core-async (13)
- # datalevin (4)
- # datomic (3)
- # holy-lambda (7)
- # kaocha (3)
- # lsp (23)
- # off-topic (39)
- # pedestal (10)
- # portal (5)
- # practicalli (2)
- # rdf (10)
- # releases (1)
- # shadow-cljs (66)
- # tools-deps (146)
- # uncomplicate (1)
- # xtdb (10)
I did not start out with Clojure but I did switch to FP langs quite early in my career, and professionally my first job was in Clojure. For me I found it quite natural to take it up because I think I had no expectations, and if anything it felt more natural to me than working with mutable languages did, and more productive too. Now I struggle to remember how to do things the imperative way, and I avoid jobs in something like pure Java, because all that imperative OOP stuff makes my brain hurt worse than monads ever did.
I failed to take to programming until I found functional programming. I couldn't make sense of it, and FP just clicked.
I'm grateful a bunch of obnoxious loudmouths on obscure forums kept shouting I should read SICP, wouldn't be here otherwise
Started with Python, then Java and Javascript. OOP never really made sense to me as a domain modelling method, and I found myself reinventing data oriented programming ala clojure, so when Clojure came everything just clicked.
I think the breaking point came earlier for me because I was doing games and its often not easy to model games beforehand. ECS helped but even then its actually just OOP with composition forced in as the main paradigm
I'm writing a small lib to add back some imperative construct, because I want to be able to practice for programming interviews in Clojure doing leetcode with it. And I realize even simple things are so confusing in a mutable world. Take this:
(var i 1)
(let [i 2]
(println i)
(var i 3)) ; <-- what should happen here?
(println i)
Imagine var is like a function local mutable var i = 1
-> (var i 1)
What do you expect happens here? Does (var i 3)
should explicitly refer to the function scope and so it should mutate the i
in the outer block? Or should it fail because in the current let
block i
is now shadowed by an immutable value?
If you go read even more on what JavaScript does for var
, let
and const
it's all so confusing haha. Add Closures to it, and it's even more weird, should a Closure capture a mutable variable? Should that not be allowed? Should it capture a copy at the time of closing over?@U0K064KQV
> What do you expect happens here? What do you expect happens here? Does (var i 3)
...
Hard to guess because you have apparently created your own private language, but not provided us the specs.
ClojureScript does not have this var
form, you describe. (Maybe you defined it in a macro? Then what "happens" depends on macro expansion.)
The closest thing to a let block in JavaScript would be invoking an anonymous function, where I would expect function-semantics to apply, along with var
's overly loose semantics. At no point would i
be immutable.
var i = 1
(i => {
console.log(i) // -> 2
var i = 3
return i
})(2) // -> 3
console.log(i) // -> 1
In practice, I would be using let
instead of var
(or more likely const
). Which would make that usage a syntax error.
(i => {
i = 3 // okay
let i = 4 // š£
)(2)
> If you go read even more on what JavaScript does for var
, let
and const
it's all so confusing haha.
It might seem less confusing if you realize that var
is deprecated in favor of let
and const
.
> should a Closure capture a mutable variable? Should that not be allowed? Should it capture a copy at the time of closing over?
Yes, you can and should be able to close over a mutable variable, and no it should not make a copy (although you might choose to copy in your code).
(defn memoize [f]
;; mutable cache
(let [cache (atom {})]
;; close over
(fn [x]
;; return from cache if exists
(or (get @cache x)
;; else mutate cache
(let [y (f x)]
(swap! cache #(assoc % x y))
y)))))
> should a Closure capture a mutable variable?
What about const
array that has mutable variables inside? There is no absolute answer to this question because language author may decide to copy the variable or to use a pointer.
You can do this thing with mutable arrays in Clojure just fine.
It doesn't mean you should but you can.
That's exactly my point, there's no logical semantics. That's what tricky about it. Immutability in contrast doesn't beg these questions, it's much simpler. In Java for example it errors if you try to capture a mutable variable in a Clojure. In JavaScript it doesn't. Then capturing a mutable variable is different from capturing a mutable container. And then in JavaScript var and let have all kind of non intuitive interaction. Which is why people now say to never use var. Etc.
> In Java for example it errors if you try to capture a mutable variable in a Clojure. In JavaScript it doesn't.
Interesting. I would guess Java side of that is an unfortunate side effect from tacking on lambda support so late in the game.
Realistically though, there is no meaningful difference between int i = 1; i = 2
and MyContainer<int> i = MyContainer<>(1); i.set(2);
, besides yet more boilerplate in an already long-winded language. (That is why I felt no hesitation in using Clojure's atom
for my example above about mutating inside a closure.)
> Which is why people now say to never use var.
As I just said, let
and const
are intended to deprecate var
. The reason people say not to use var
has nothing specifically to do with weird interactions with let
. The design of var
is pretty universally considered a mistake, but changing it would break backwards compatibility. let
and const
were specifically intended to replace var
from the outset.
Unless you find yourself working in legacy JavaScript, you should not need to know or care about what var
does.
> there's no logical semantics.
Patently untrue. Those semantics are laid out in excruciating detail here.
https://www.ecma-international.org/publications-and-standards/standards/ecma-262/
There are holes in the logic, but that is unavoidable. I challenge you to name any language in which you cannot find some case of unspecified and/or explicitly undefined behavior. None of those holes relate to the discussion brought up so far.
I certainly agree the semantics of JS are far too complex, with too many surprising rules. Not sure how much of that is due to mutability and how much is due to other factors like:
ā¢ JS was created in only a week and a half, with a mandate to make it look like Java even though it was a derived mainly from Scheme.
ā¢ For most of the last 30 years, numerous competing parties have haphazardly tacked features onto JS with the intent of standing out in the market. Standards were then occasionally re-specified to conform to in-the-wild usage of all these competing JS supersets. (Only recently did major browser vendors choose to mostly work together here.)
ā¢ Every new version of the spec prioritizes backward-compatibility above all else. Up through ES6 that would frequently include canonicalizing broken implementations of older specs (because presumably some website somewhere is dependent on that breakage). Anywhere one major vendor implemented a feature correctly while another one did not, all reasonable effort was made to support both implementations in parallel.
ā¢ The spec is written by commitee, generally leading to subpar compromises that don't quite satisfy anybody, but is hopefully better than not making any changes.
Don't misunderstand me, I šÆ agree that immutability adds simplicity. But it's not a lack of semantics that make it difficult to manage state.
I think you might have missed my point š. You can define many possible semantics, that's why it's complex. No semantic are arguably correct, they're just what they are. This is because the mutability and the interplay with scopes and execution timing adds complexity and creates weird edge cases that you have to make a decision as to what to do about it. Different languages will make different decisions. I'm using JS as an example, where they had one set of semantics for mutable variable using var for many years, and recently thought ok maybe those create too many programming mistakes and is too confusing, and now attempted a second set of semantics with let/const. This does create some new edge cases which they also had to make decisions about, that's what my example shows. Vars are function scoped, you could choose to allow defining a function variable inside a let block, or you could choose it would be too confusing and not allow it. Both would be correct, it's just a design decision. Now the thing is as a user, it's not intuitive which will have been chosen by different languages. Again, that's my point, too many edge cases and decisions of how the language should handle them, so as a user, it's very confusing and you're kind of left wondering often, wait, so here what's going to happen? And it's not always obvious what will.
My point isn't that the semantics are not properly documented, just that there's a lot of edge cases, and therefore it makes the semantics complicated, which in turn means a lot of ambiguity is created as to figuring out which semantics apply and how it'll behave.
> JS was created in only a week and a half What about standards and all other development? I wouldn't call this "week" or even year, or even 10 years. Wikipedia says it's there for 26 years. So no, it's not a week and they had time to rethink it completely.
@U028ART884X See the other bullet points: "numerous competing parties have haphazardly tacked features onto JS", "written by commitee", "prioritizes backward-compatibility above all else". Yes. JavaScript has changed over the years. But the original work is mostly still there, enshrined in all it's glory. (Ignoring that initial attempts to actually implement it in Netscape probably forced some early refinements) the history of JS is largely one of non-uniform additions, (via conformance of hostilely competing forks) and conformance of the spec to reclassify implementation bugs as valid. Any attempt at "Wouldn't it be better if...?" is traditionally met with either dogged ideological disagreement or "Oh God! Yes! That would be so much better... Except we know of at least N existing websites that would break if we did that." This means you either get no change or a new feature living beside the old. Or worse, you get new behavior squashed on top of old behavior in an unholy way, with lots of weird new edge cases to worry about.
This wasn't intentional but when I added syntax highlighting to keyword namespaces then I got Clojure's colors š Colorscheme: https://github.com/srcery-colors/srcery-vim Edit: This is not yet merged into clojure.vim so nobody can use it :thinking_face:
bit of a lazy-web q but here it goes. Say I have a few .java files in my clj project, how would you lint them? Can I expect useful insights, or it's not as worthwhile as it is in clj?
Wouldn't LSP server try to simply "work"? :thinking_face:
I have no idea about what LSP does for .java files (presumably clojure-lsp does nothing - it's not its job) also, I'm primarily looking for something I'd set up in CI.
I was thinking about Java's LSP server. But probably it wouldn't work because to find that Clojure classes exist (:gen-class) it needs to build the JAR and generate classes :thinking_face:
I can imagine it working, .java files are just .java files. One could just specify one's java source paths to LSP and pretend the rest of the clojure project doesn't exist.
That's what I was thinking too. But then Clojure wouldn't be readchable and the integration point would constantly show errors.
I didn't try this and I talk here on purely theoretical level š
#cursive does that š BTW, you can find a few "java" topics in clojure-lsp changelog https://github.com/clojure-lsp/clojure-lsp/issues/762 Simple things like autocomplete for java class in clojure file and go-to-definition from clj to java should work.
my question is different :) the tldr is "what's a good linter for .java files". clj has almost nothing to do with this
Are there any clojure podcast episodes that are laser focused on teaching you how to build something? And they don't spend much time chatting about motivations or history or social context.
Hey Lyn. on the code again may interest you. Some recent episodes are exactly what you describe. https://www.youtube.com/c/onthecodeagain https://www.youtube.com/watch?v=0mrguRPgCzI https://www.youtube.com/watch?v=V-dBmuRsW6w
I'm not sure podcast is a good medium for whT you are asking. The closest one I can think of would be Functional Design in Clojure
https://clojuredesign.club/ . Not exactly deep. But mostly sticks to "This is how I might build X."
Cool. Yeah podcasts are not ideal but some pull it off, mobycast comes to mind in the aws space.
nice idea actually, I would consider paying a patreon account to someone who writes or shows mundane stuff like "how to create a cron replacement in clojure" or "handling promises with error handling and cancellation with cljs"
Tried out functional design in clojure last night, that one's really good, thanks everybody.
Yeah it's the best, I love that podcast
Anyone have any recommendations for email services that let you send emails via API to a custom SMTP server (ie. through Amazon SES), but then also keep track of all of the emails being sent? So something with the same interface as https://github.com/drewr/postal but with an accompanying outbox UI you can check and query for sent emails?