Fork me on GitHub
#beginners
<
2020-01-02
>
jon92005:01:24

Does anyone here use Ruby, Elixir, and Clojure? I’d be curious to see how Clojure compares (but not quite curious enough to knuckle down and learn Clojure quite yet)

didibus06:01:00

What are curious about?

didibus06:01:01

Performance?

nate_clojurians06:01:20

I've done all three professionally and I greatly prefer clojure. clojure is more similar to elixir in language semantics but elixir forces you into the actor model and OTP is a mountain of implicit behavior that's hard to reason about or debug. clojure feels a lot more flexible and has the entire jvm ecosystem to lean on. there's not nearly as much in the erlang/elixir ecosystem

nate_clojurians06:01:40

the flexibility of not having to use the actor model is huuuuuge. so many problems don't fit the actor model.

jon92006:01:32

Hmm interesting. How do you find the ergonomics of reading/writing it? I saw that Rich Hickey is a fan of named params where position doesn’t matter, so I figured it must be in Clojure. Elixir doesn’t have that

jon92006:01:27

Sure performance is a good one. Can you get microsecond response times from Clojure web servers? Erlang uses a fancy setup where it streams IO Lists to the TCP socket instead of concatenating strings. Saves on CPU and memory. Or maybe since Clojure is on the JVM, CPU performance is great enough to just chew through string concat.

jon92006:01:03

I’m also curious about interop with JRuby, come to think of it 🙂

jon92006:01:07

What else… what are the most common IDEs and editors? And what does deploy look like for a production web app? I haven’t done Java in many, many years.

tkjone13:01:13

@jon920 > What are the most common IDEs and editors? This post has a full list https://betweentwoparens.com/clojure-text-editors

didibus06:01:31

I only have toy experience with Elixir and Ruby, while I do have professional Clojure experience, so take the following as you want. My favourite of the three is Clojure, followed by Elixir and then Ruby. That implies I like Clojure ergonomics the best, but I think they take a long time to get used too. REPL driven development is central to the ergonomics, and getting used to the Lisp syntax, infix notation, nested expressions. There's a lot more to get adjusted too before the ergonomics click

rakyi10:01:05

You probably meant prefix (Polish) notation.

didibus06:01:15

Clojure does have positional args as well as named args, and anything in between. If you're curious, this post of mine: https://www.rubberducking.com/2018/04/overview-of-clojurescript-110-features.html is a short read, and goes over very quickly the basic features of Clojure (well its ClojureScript in this case, but they're the same).

didibus06:01:28

So that should give you a good idea of the basic ergonomics

didibus06:01:10

For performance, I just wouldn't worry. Clojure is the fastest of the three. It will beat ruby and elixir at single and multi-threaded CPU performance, as well as can make use of shared memory.

didibus06:01:53

Not sure what you mean related to the socket streaming. You can stream whatever you want over a socket.

didibus06:01:59

JRuby interop is not something I tried. I did do Scala, Java and Kotin interop. Generally, to interop between these you basically put java in between, since they all interop with Java. So I'd assume JRuby would be the same.

didibus06:01:33

Basically, if JRuby can make use of a Java class, well you'll be able to use Clojure from JRuby, since Clojure can expose itself through a Java Class or a Java object.

didibus06:01:01

And similarly, if JRuby can expose itself as a Java class or object, Clojure will be able to make use of it through that.

jon92006:01:13

Thanks that’s great info. What are a few of the most well-written clojure projects? In terms of code aesthetics and software architecture

didibus07:01:40

Most of Clojure is written in Clojure, though implementing language constructs is definitely hairier. I heard people recommend cljdocs before, but I never checked its source personally: https://github.com/cljdoc/cljdoc

jon92007:01:27

Neat reading the core lib source. It’s surprisingly clear enough. Might have to dig into Clojure at some point here. I also wonder if we can get it on Graal VM alongside TruffleRuby

didibus17:01:09

I'm not sure. Clojure doesn't have a Truffle based implementation. I'm not sure Truffle is appropriate either, since Clojure is compiled. But if Truffle based lang can interrop with Java, they should be able to interop with Clojure as well.

joni.korpelin09:01:34

Most used ides/editors are probably: • Emacs • Idea with Cursive • Vim with Fireplace? • VsCode with Calva

didibus17:01:44

I'd say so. Atom with Chlorine is popular as well.

ales.najmann09:01:51

• Neovim with Conjure

guy10:01:38

is it worth mentioning lightable? I found it super handy as a beginner

victorbjelkholm42911:01:55

worth clarifying: lighttable is a clojure editor, written in clojure. So handy as an editor as a beginner, but also useful for reviewing well-written Clojure code (cc @jon920)

guy11:01:45

Nice 👍

slawek09812:01:13

But unfortunately it's a bit dead as of now.

guy12:01:00

oh really? thats a shame, i really liked it when i used it.

slawek09813:01:21

Last time I checked it was still using some deprecated Electron with multiple issues due to that and with no workforce to rewrite it.

jon92017:01:39

The LightTable code is great! Thank you.

mawiraike12:01:31

I would like help understanding why in the following code, the function 'custom-inc' never gets evaluated.

(defn custom-inc [pre]
  (if (nil? pre) 0 (inc pre)))
The workaround is choices is a string got from looping a seq with 'do-seq'
(send (agent {}) assoc choices custom-inc)
Returning the agent using '@' macro returns the values of the map as objects and not integers as intended. E.g
{"Android Development" #object[csv_parser.core$custom_inc 0x106c180 "[email protected]"],
 "Machine Learning and Artificial Intelligence" #object[csv_parser.core$custom_inc
                                                        0x106c180"[email protected]"],...
}

bronsa12:01:44

looks like you may mean to use update instead of assoc?

mawiraike12:01:25

Ah, yes. Thanks. 😅

jon92017:01:27

Does Clojure have an automatic code formatter tool? I’ve been using one with Elixir and it’s made onboarding a lot easier.

didibus18:01:28

cljfmt and zprint would be the main ones. I use cljfmt personally. Simple and straightforward.

noisesmith17:01:26

there's cljfmt, but that just does indentation and leaves line-breaks as is last I saw

noisesmith17:01:45

clojure's formatting idioms (like any lisp) are much simpler than most languages

jon92017:01:20

Interesting. I’m pretty sold on investing some time on Clojure but someone just threw me a curveball which is Scala. Obviously there are huge difference in syntax, and Scala also does OOP. But what other differences exist between the two? They seem to occupy a similar niche being FP languages on the JVM (from my complete newbie point of view)

bfabry17:01:31

aside from many other major differences in philosophy, scala is big on static typing while clojure is dynamic

seancorfield18:01:54

I tried introducing Scala at work back in '09 but the long edit-compile-deploy-test cycle didn't sit well with folks here who had worked with dynamic languages, so we switched to Clojure.

seancorfield18:01:37

Scala is a hybrid language so you can use it as just a "better Java" without leveraging the FP aspects. It's a very complex language too.

didibus18:01:19

I'm not a fan of Scala

didibus18:01:38

So I can't recommend it. Implicits are the worst language constructs I've ever seen

seancorfield18:01:39

Scala has also suffered from a lot of backward compatibility problems over various releases, often forcing you to upgrade your entire toolchain just to use a newer version of the compiler (the 2.7 to 2.8 upgrade in particular was just horrendous!).

chepprey18:01:04

I'd sooner choose Kotlin as a "better Java" before I'd choose Scala.

seancorfield18:01:18

Yup, I'll agree on implicits. They seem so, so convenient but they make code really hard to understand and reason about. So much "magic".

seancorfield18:01:49

Yup, I'll agree on Kotlin too. I think it's a much nicer language than Scala. That said, I haven't used Kotlin in production (but I have used Scala).

ghadi18:01:11

Java is going to subsume all of those languages besides Clojure

jon92018:01:58

Oh no Scala has a long compile time? I can’t go back after seeing what is possible with Go.

didibus18:01:00

I can see Kotlin thriving due to Android

bfabry18:01:16

lol. with regards to that. we chose java over scala for that reason. "oh java 10+ gives us all the stuff we want from scala anyway". but now we can only use java 8 because we need spark and spark is written in a version of scala that only supports java 8 and it's a massive project to rewrite of spark in a newer version of scala that supports a newer version of java so that we can write spark code in modern java 😆

didibus18:01:32

I find Scala is a bit of a monster. It does OOP, it does FP, it tries to add extra typing to the JVM, the combination feels a bit chaotic. Like a jack of all trade, master of none.

noisesmith18:01:16

I've heard second hand that with prudent use, Scala can be your ideal language, until you need to interact with someone else's Scala lib (and their ideal language is a different subset of Scala) - sounds a lot like the situation with C++

didibus18:01:17

And Java interrop isn't very good as well

seancorfield18:01:11

As someone who spent nearly a decade doing C++ (and was on the ANSI Standards committee for eight years), I don't think C++ even comes close to the complexity and multiple-dialects that Scala suffers from...

noisesmith18:01:12

one major benefit with Clojure is that interop works great both ways - it's designed to be simple to use Java libs from Clojure and to be simple to use Clojure libs from Java

zygon418:01:45

And Clojure supports basic OOP concepts if you want them.. this shouldn't be OOP lang vs non-OOP lang

noisesmith18:01:28

that's true, so long as you agree with me and other right thinking people that enheritance is not a core OOP feature :D

zygon418:01:39

I don't even know what OOP is anymore

zygon418:01:36

Most reasonable people avoid the original OOP concepts. OOP nowaways sounds like "this is OOP, therefore everything must live in a class, classes everywhere!"

didibus18:01:55

Had a big convo with noisesmith about this just the other day 😛

didibus18:01:15

I think of OOP as objects everywhere

zygon418:01:37

A little meta: I think classes/objects by default is what people consider casually as "it supports OOP". ie mandatory classes = safe, OOP

zygon418:01:10

I think we're saying the same things

didibus19:01:33

I see an object as a self-referential collection of fields and methods. Like a data-structure that stores key/vals where vals can be values or functions. And where the functions inside the values have a magical implicit reference to the object, so it can refer to other keys in the object and make use of them.

didibus19:01:05

Oh, a mutable data-structure as well.

didibus19:01:21

And then OOP languages, are those who build upon this fundamental data structure

didibus19:01:34

They don't have to have classes

didibus19:01:58

Its actually quite a tricky structure. I think to make objects really concrete in your head, it helps to try and implement one. That self-referential property isn't obvious.

didibus19:01:13

And it is what separates an object from a mutable Map

didibus19:01:13

Anyways, that's only my chosen taxonomy. But I find it makes for a good model, and what I like about it, is from that angle, it makes sense that JS, Python, Java, are OOP, and that Clojure is not.

alper19:01:39

I consider Scala an anti-pattern for many of the above mentioned reasons. I’ve seen several shops that programmed themselves in a corner when it comes to comprehensability, being able to hire etc.

jon92018:01:09

How about Kotlin vs. Clojure? Not as a battle but a comparison of differences and trade-offs

alper19:01:48

I’d add there that for instance Kotlin and Swift which look very much the same are already worlds apart. Swift has a lot stronger guarantees on immutability.

zachmclark18:01:12

kotlin is java with less characters to type, if you want a new language but don't want to change anything about how you approach problems or write your code, it's safe

didibus18:01:21

I think it'll help you if I just described Clojure vs non-lisps 😛 Clojure is homoiconic, meaning that the same syntax is used to write code, as well as to represent data. Think of it like if all JavaScript code was valid JSON. This trait is unique to Clojure (and only a handful of other langs, mostly lisps). Since code is just data, it allows for very easy introspection facilities, and from that comes Macros. This enables a style of programming called Meta-Programming. That's the first thing that Clojure vs other-langs has. Clojure is a Meta-Programming language.

ghadi18:01:58

You will grow more as a programmer learning Clojure, but language choices rarely happen in isolation -- are you planning on tackling a problem, or just as a learning endeavor?

jon92018:01:45

I’m looking to add another tool to my kit to invest in long term. Right now I have Go for CPU bound tasks and easy deploy. Ruby for general purpose. Erlang/Elixir for distributed and web performance. Javascript when forced to, or in the future when it gets pipelines and pattern matching. At this moment I’m actively working in Ruby, and spending a great deal of time learning Erlang/Elixir on the side. I’m already sold on keeping a close eye on Clojure for sure, just trying to get a grip on differentiators from sidelines without actually programming it yet.

didibus18:01:46

The next one is the Lisp syntax. It uses infix notation, that's a big difference from other langs. (+ 1 2 3 4) instead of (1 + 2 + 3 + 4). Another thing with the syntax, it is very structural, and that allows for structural editing. You don't edit it using lines and spaces like other langs, instead you move up and down through it like if it was a Tree.

seancorfield18:01:18

You mean "prefix notation", not infix.

didibus18:01:00

Another big one is the REPL and the evaluation model. In Clojure, compile-time IS runtime. It doesn't even make sense to say compile-time. You need to think in terms of phases: read phase, compile phase, macro-expansion phase, evaluation phase. And all phases happen at runtime. It is a true dynamic language, not just in the sense of having dynamic types, but you don't go from source -> compiled form -> running form. You start with a running empty program, and as it is running, you add things to it, or modify existing things, or take things away. This is the foundation of the REPL driven development and is very different from other langs.

didibus18:01:23

Now another difference, is even though it has all this dynamism, it is in fact, a compiled language, unlike most other dynamically type langs such as Python, Ruby, JS, etc.

alper19:01:36

What does it compile to again?

didibus19:01:48

JVM bytecode or JavaScript or .NET IR depending if you are using Clojure, ClojureScript or ClojureCLR

alper19:01:21

Ah that way. So you have Clojure compiled or you have the same code interpreted in the REPL?

didibus19:01:56

Clojure compilation happens at runtime. So the REPL compiles the code as it is being sent to the repl.

seancorfield19:01:16

Clojure is never interpreted -- yeah, what he said.

seancorfield19:01:12

The Clojure compiler works on a form-by-form basis, not a file, so the REPL and "running a program" work the exact same way.

seancorfield19:01:19

That's why you can REPL into a live, running process -- even a production process -- and redefine functions on-the-fly, and it updates the bytecode (compiled, in-memory).

alper20:01:04

The REPL also compiles to byte code every time you type something in?

didibus20:01:27

Yes it does, like Sean said before, Clojure is never interpreted, always compiled.

seancorfield20:01:32

@ Stu Halloway talks about this in, I think, his REPL-Driven Development talk. He says we don't really need files etc since we could just feed our entire program to the Clojure REPL, one form at a time, in the right order, and it would have exactly the same semantics as Clojure "running" the program from the files.

seancorfield20:01:18

If you type in (defn foo [n] (inc n)), it compiles immediately to a class named user$foo (assuming you're in a REPL in the user ns). You can see that by typing (class foo). If you type (ancestors (class foo)) you'll see all the interfaces implemented by that class and all the classes that are extended by it...

alper11:01:41

And what does Clojurescript compile to then?

seancorfield16:01:59

ClojureScript transpiles to JavaScript @ (I don't use cljs so I can't provide more details than that).

didibus19:01:45

You can use: http://app.klipse.tech/ too see what ClojureScript compiles too.

didibus19:01:16

In the above case, (defn foo [n] (inc n)) becomes

cljs.user.foo = (function cljs$user$foo(n){
  return (n + (1));
});

didibus19:01:26

And in JavaScript, functions are already Objects. So in both cases, functions get compiled down to Objects of the underlying language.

seancorfield20:01:37

Much as I'd like to consider JS to be the "assembly language of the web", the pedant in me has a hard time describing ClojureScript as a compiled language (hence my use of "transpiled") 🙂

didibus21:01:16

Some people consider GCC a transpiler 😋

didibus21:01:39

But in this case I just didn't want to confuse with semantic squabbles

seancorfield21:01:28

(compilers, transpilers, and interpreters is what I used to do for a living back in the late 1980s which is why I'm a bit pedantic about it)

didibus08:01:13

I think the official ClojureScript doc calls itself a compiler. And so does the Google Closure library and for example Babel. And all these things just output JavaScript. So I feel the line between compiler and transpiler has really blured and it seems more and more those are used interchangeably. Even Emscripten calls itself a compiler, but it actually compiles C/C++ into JavaScript! So its actually going from a lower level to a higher level language 😛

didibus08:01:19

Crazy times we live in

didibus18:01:41

Clojure is not interpretted

didibus18:01:42

Also, it is an expression based language, it has no statements. That's a big difference too. It means that everything can be composed and nested one inside another.

didibus18:01:08

And now I just covered the unique parts of Clojure related to it being a Lisp. But it is also a Functional first language with support for lazy and eager evaluation, with immutable data-structures, and support for full multi-threading and concurrent programming as well. It runs on more than one host platform: CLR, JVM, JS officially, with a few more unofficially: Erlang, C++, Go.

didibus18:01:18

It also happens to have multiple-dispatch polymorphism (what made Julia famous), and traits based polymorphism, among others. And this is where I could go on an on, like the fact it also supports Logic programming, CSP, and others...

jon92018:01:31

Are there libraries that support a BEAM style of lightweight VM processes and supervision trees? Being functional it seems possible.

didibus18:01:46

Ya, meant prefix

didibus18:01:09

@jon920 core.async adds lightweight processes, but there's no similar OTP for core.async. There's a few libraries that build on top tying to add their own best practice to it, but nothing super official or popular

didibus18:01:15

Also, nothing quite like what the BEAM does, though I think Pulsar/Quasar are tying to add that to the JVM, dunno

hiredman18:01:57

core.async is more like go than erlang

hiredman18:01:07

(unamed processes communicating via queues that are values, vs named processes communicating via implicit queues)

didibus18:01:55

Its kinda getting into the details of what we mean exactly by "lightweight process", but generally, people mean less resource heavy than an OS thread, and core.async fits that bill. But if someone means something more akin to Actors, where processes have state and communicate with each other only through messages, and that there is a GC per process, etc. ya Clojure doesn't have that as far as I know, except for Clojerl off course 😛, the Clojure dialect running on BEAM

didibus18:01:09

There's a rational behind it as well: https://clojure.org/about/state#actors

didibus18:01:52

Rich Hickey doesn't like the downside of actors, that when they don't help, they make things slower and more complicated. And that they don't actually extend from local to network magically as advertised.

hiredman18:01:53

The Language of the System - Rich Hickey

didibus18:01:15

I need to re-watch that one, don't remember the content!

didibus18:01:29

Maybe January 2020 I'll have myself a little Rich Hickey maraton 😛

hiredman18:01:33

basically what you just said

hiredman18:01:51

just linked it as a reference for drilling down

didibus18:01:20

Hope he has a new talk this year haha, I need my Rich Hickey fix

andy.fingerhut19:01:49

If you prefer reading to listening/watching, and want a few extra references/links thrown in for good measure, there are transcripts of most of his talks here: https://github.com/matthiasn/talk-transcripts/tree/master/Hickey_Rich

jon92019:01:56

Are there any other languages that blow your mind like Clojure, and are also robust and production ready? I’m eyeing ML languages like Haskell (but haven’t dove in because I hear it compiles slow), and maybe a Prolog like from some of these new databases I’ve seen.

noisesmith19:01:41

Rust might be interesting here - some ML influence, good for systems work

seancorfield19:01:57

@jon920 Mind-blowing languages: definitely Prolog, definitely Haskell, maybe Elm, Rust, APL...

ales.najmann19:01:00

I think Dotty is a great thing. They recently announced feature completeness of the new compiler.

ales.najmann19:01:16

Dotty = new compiler for Scala

noisesmith19:01:25

also, Ocaml is an ML that compiles very quickly if that's the main concern (it generates fast code too)

ales.najmann19:01:46

Multiprocessing story of Ocaml is quite weak though.

ales.najmann19:01:40

I see F# as more practical ML implementation. Even syntactically is less bizarre.

seancorfield19:01:57

(maybe F# -- also in the ML family -- but for its Typed Providers stuff?)

jon92019:01:45

I’m avoiding Rust because it compiles slow, I’m guessing because the language is so complex. Trying to avoid waiting on compilers in the next decade of my life.

ales.najmann19:01:45

Rust make you ❤️ garbage collector 🙂

andy.fingerhut19:01:30

C even more so 🙂

ales.najmann19:01:03

Some people talk nicely about Nim https://nim-lang.org

didibus19:01:35

The thing is that Rust has a claim that unlike Haskell, I believe in. Trade some convenience for the borrow checker, and you get memory safety without overhead.

didibus19:01:49

When this is what you need, it is pretty awesome

didibus19:01:52

Haaskell says, trade some convenience for pedantic static type checking and have less bugs, but I don't believe it truly reduces bugs, so I don't find that trade to be a good one. Rust on the other hand makes a trade that I can see taking.

ales.najmann19:01:27

I tell you what the problem is here. I was talking to Phil Wadler when he was in Prague. I asked him what he thinks about linear types. That's what Rust is based around. And he replied that "it's not worth it". Of course this is argument by authority and maybe he would even dismissed my quotation as it is written here. But I actually have feeling it's huge and inconvenient trade off.

ales.najmann19:01:23

Some even simple things are unnecessary complex with Rust. And their Result type is leaky because of unwrap etc.

ales.najmann19:01:12

But I'm not expert at all. I wish Rust shiny future, because it is quite modern compiler. It would be nice if it succeeds!

didibus19:01:53

That's fair. I mean, for many things, a GC is just better. But when you need something more performant, that's when I feel it could be worth it. Also, Rust types are affine, slightly more convenient to use then linear types. Some people might argue that, something like C++ unique_ptr is enough, so you lean on move semantics only when needed, instead of Rust's default to move semantics. We'll see, every new release of Rust they make it slightly more ergonomic. Lets see how far they can take it.

didibus19:01:16

My personal fav is hardware accelerated GC. I hope this becomes standard in the future. I saw some paper on it, and they achieve such better performance, almost eliminate completely the GC overhead.

zachmclark19:01:58

I think I've tried to pick up Rust once every year for the past few years, the complexity is ridiculous, the language is optimized for maximum cognitive overhead

didibus19:01:35

My other languages of current interests are Rust and Red. I'd say Prolog, J, Racket, WebPPL and Forth are all mind blowing as well.

seancorfield19:01:03

+1 for Forth as mind-blowing.

didibus19:01:08

Well, I should mention Carp, which is a Clojure-like Rust 😛

seancorfield19:01:09

Such a shame this never took off https://gershwin.github.io/

seancorfield19:01:55

(for a while, we actually used Gershwin instead of Clojure and had some stack-based code... but switched back to pure Clojure as it diverged from the version used in Gershwin)

kamuela19:01:47

https://stackoverflow.com/questions/38499290/digits-back-to-number-again is there a digits function as the question author mentions? Or is he neglecting that he wrote it himself?

noisesmith19:01:08

there's no digits function, IIRC this is one of the puzzles from http://4clojure.com

noisesmith19:01:47

looks like the question was edited to include the digits function

kamuela19:01:49

@ thanks for pointing that out, totally missed that

johnjelinek21:01:37

@seancorfield: I'm using clj-new to scaffold a new app, and when I'm trying to run the uberjar, I get an error. Here's how I got it:

$ clj -A:new app a-b-c/Fixed-DHCP
$ cd Fixed-DHCP
$ clojure -A:uberjar
Compiling a-b-c.Fixed-DHCP ...
Building uber jar: Fixed-DHCP.jar
Processing pom.xml for {a-b-c/Fixed-DHCP {:mvn/version "0.1.0-SNAPSHOT"}}
$ java -jar Fixed-DHCP.jar
Error: Could not find or load main class a-b-c.Fixed-DHCP

johnjelinek21:01:15

I suspect the issue is with converting - to _, but I'm not sure where that _ needs to be replaced

seancorfield21:01:01

Yeah, right now depstar doesn't do that translation. I'll create an issue to address that...

johnjelinek21:01:13

much better in 0.5.1

seancorfield21:01:00

And clj-new updated to 0.8.4 to include the fixed depstar too!

seancorfield21:01:05

Thanks for spotting that!

johnjelinek23:01:38

is a macro the right way to turn:

{:end-point "abc"}
into
(-> (builder)
    (.endPoint "abc")
    .build)
?

johnjelinek23:01:34

there's some java interop builder stuff I'm trying to work with, but I'm tired of writing (.something ,,,) over and over and I'd rather just use data

noisesmith23:01:07

there's definitely prior art to doing things this way

johnjelinek23:01:03

got any links?

didibus00:01:14

I mean, for that specific case, a function should do

noisesmith00:01:33

@ I think the intention here is that the :foo-bar keyword be translated to a .fooBar setter method

noisesmith00:01:58

@ I looked but didn't see it in the place I expected

didibus00:01:30

Oh I see, like an auto-mapper thingy

noisesmith01:01:15

the camel-snake-kebab library does the kind of key - method name translation you'd want at least https://github.com/clj-commons/camel-snake-kebab

noisesmith01:01:48

then it's just a question of doing that parsing / translation to build the final form (you might want eval too here...)

hindol.adhya16:01:40

I have no experience in writing macros. Can you explain why an eval is needed?

noisesmith18:01:50

macros can only operate on literal data in a source form, in order to construct method calls out of keywords that are not literals (eg. if they come from some var or data somewhere else), you need eval (as new method calls can't be created at runtime without expensive and complex reflection)

hiredman23:01:47

macros are functions that take values at compile time and turn them in to other values, that are then fed in to the compiler

hiredman23:01:16

the problem with using macros for that kind of thing is it limits you to dealing with data that is static and known at compile time

hiredman23:01:47

the only reason people reach for macros for that kind of thing is because they are scared of using reflection

johnjelinek23:01:12

ok -- so, what's the reflection-y way to do this ☝️

seancorfield23:01:43

I'd have to check whether org.clojure/java.data supports builders... I know it supports setters...

hiredman23:01:16

something like

(defn f [m]
  (let [b builder]
    (doseq [[k v] m]
      (clojure.lang.Reflector/invokeInstanceMember
       (clojure-to-java-name (name k))
       b
       v))
    (.build b)))
you'll need to write or find a clojure-to-java-name

seancorfield23:01:38

Is there a nice, simple builder in the standard Java library I can try it out on?

hiredman23:01:33

you could also write a function that creates the form you want and just call eval on it

seancorfield23:01:50

Well, that has setters so I know that will work.

johnjelinek23:01:09

(-> (new java.util.Locale$Builder)
    (.setLanguage "en")
    .build)

seancorfield02:01:04

I have a working version of clojure.java.data in git that supports this:

org.clojure/java.data
{:git/url ""
 :sha "20d8562bf451f41bf58ac0b38401648ba21232a3"}
in deps.edn and then
(require '[clojure.java.data.builder :as builder])
(builder/to-java java.util.Locale {:language "en"})

johnjelinek04:01:52

@seancorfield: how would I call (builder/to-java ,,, if my builder is actually initialized like this: (ABC/builder) instead of (new ABC$Builder) ?

johnjelinek04:01:15

I get this error: > Error: > Execution error (InstantiationException) at sun.reflect.InstantiationExceptionConstructorAccessorImpl/newInstance (InstantiationExceptionConstructorAccessorImpl.java:48).

seancorfield04:01:28

A static method on the parent class?

seancorfield05:01:15

Thanks. Yeah, I'm going to need to support building on an instance in cases where I can't just construct the builder class... gimme a few...

seancorfield06:01:17

OK, the latest version on GitHub supports (builder/to-java ABC (ABC/builder) props opts) so you can pass in a builder instance if it can't be directly constructed from a class.

seancorfield23:01:00

Ah, actually, no. java.data expects the original object to be mutable and the setters to return void. So it doesn't work. Hmm...

seancorfield23:01:39

Might be a nice enhancement to support that...

hiredman23:01:58

(the doseq in the above will need replacing with reduce then)