Fork me on GitHub
#off-topic
<
2022-07-29
>
teodorlu20:07:29

possible tagline: Clojure: abstract over whatever the fuck you want

pppaul21:07:42

racket takes the cake on that 🙂

Martynas Maciulevičius03:07:27

How? They're both lisps

teodorlu08:07:10

I'd be willing to share 😊

pppaul14:07:43

clojure is very restrictive compared to other lisps, and racket has gone to the extreme in terms of supporting/making DSLs.

Martynas Maciulevičius14:07:12

And by restrictive you mean that you want to have continuations and procedural nature?

pppaul14:07:33

clojure had a clear goal of being a good solution to concurrency problems on the JVM (wrt java). it was, and still is, very restrictive in macros. custom reader macros are fairly new. if by continuation you means something like "yield", that is not what i'm referring to, sounds like something that you should be able to do with macros. i think clojure is very procedural, and if you want examples of languages that aren't i can provide. i actually like clojure because it's procedural, and has barebone macros.

Martynas Maciulevičius15:07:17

So Racket is not procedural? I didn't try it but the wiki page says it's based on Scheme. And scheme is procedural. You already mentioned Racket. I don't need more languages. I only comment here to understand what you find more important in Racket that you don't have in Clojure. So let's not enlarge the problem and make the comparison simple. I've read about Racket that when you create custom reader macros your libraries tend to be less reusable because now you force the reader(interpreter) to behave in multiple different ways which can clash. So let's make the comparison simple - what do you have in Racket that you want and you are unhappy about in Clojure (after all we're here in Clojure's forum)? Also I think it's only fair to compare LISP-like to LISP-like. Because even though Haskell is "safe" or Rust is "safe" in its own way and has "macros" they're not the same as in LISP. If we'll go into multiple-language mode then the your problem will get even more abstract and you'll not understand it either. Or maybe you do understand, then tell what it is.

pppaul16:07:30

i didn't say anything about racket being procedural. i said it is very good at making new languages (i think that is fairly high in the ability to make abstractions). your comment about racket macros is exactly what i meant by clojure being restrictive. "I don't need more languages." that's going to limit you on what problems you are going to be able to solve, and who you can solve problems for. i'm not unhappy about clojure, but i've written languages where clojure is the host, and i think this is where racket is good. i've also used clojure to talk to languages that solve problems clojure can't solve (floating point constraint solving). i brought up racket because it is lisp, and has different macros from clojure, clojure has unhygienic macros, and i like them cus they let me do hacky things. here is a list of projects clojure would struggle to duplicate https://beautifulracket.com/appendix/domain-specific-languages.html "racket-implemented-dsls"

pppaul16:07:31

also, don't take me wrong, i don't mean restrictive is bad, i consider it good. i prefer clojure because i can do very hacky things when i want, and i can do nice functional things when i want. sorta a Frankenstein approach. however i got into lisp because i wanted to make languages to solve my problems, and i have done so with clojure. i think clojure is not very good at doing that. clojure has a lot of things that i miss when i go to other languages, but depending on my problem i look for tools that are good at solving it, then maybe i use clojure along with those other tools.

teodorlu17:07:39

While embedding a completely different language is better suited to Racket than Clojure, I think the emphasis on "it's just data" can be a better alternative to a macro-based DSL. I don't think "it's just data" covers every case, though. Got any specific examples of problems / DSL approaches where Racket shines and Clojure gives you trouble?

pppaul17:07:59

yeah, I wrote a non-sexp declarative language in clojure, I think racket is better at that. embedded DSLs are a compromise, and sometimes not really an option. also in my case I tried to make an embedded DSL as a first attempt, abandoned it fairly early.

teodorlu17:07:26

> embedded DSLs are a compromise Riiight, in Clojure you're essentially restricted to embedded DSLs. With the advantages and limitations that brings.

pppaul17:07:24

it would be really nice if clojure had first class dsl support, though. be very cool to have compile errors on something like that. debugging any sort of dsl in clojure, or most languages, is very difficult

Martynas Maciulevičius17:07:24

People use specs to enforce DSL correctness. But that's type information which is not the actual code :thinking_face: With custom macros you have a way to fail at the compilation step :thinking_face:

pppaul17:07:43

I have used spec in one of my DSLs, but it's a lot of work in comparison to writing a grammar like what you see in datomic, and I've found writing instaparse grammars much easier that spec. it's still not really good enough for error reports. and if you ever make a mistake in you let statements you can see that.

pppaul17:07:18

macros let you fail in very spectacular ways, because you can give them code that they parse correctly but produce nonsense output... sorta like an sql injection attack. someone tried to fix this year's ago via "illuminated macros", but it seems abandoned

Martynas Maciulevičius03:07:44

I very largely avoid macros in Clojure. I only resort to macros when I totally need them (almost never) and write my DSLs in basic data structures. One benefit of that is that if I do my DSL in a particular way then I can allow it to be parsed from JSON (you can't parse a set from JSON list though). And that means that I can expose it to non-clojure environments where there can't be Clojure devs because you can't hire 20 Clojure people. I don't think macros are that good here as you'd need to do custom deserialization (I do it via spec lib but then I operate on data and do some loops or other things to the input; but it could probably be interpreted into macro calls -- but then it's calls and not data, then it starts to smell as imperative paradigm...). Well... it can be based on macros but you would need to have data structure representation anyway so macro layer is kind of redundant here. But you could use macros inside of the code and nobody would care :thinking_face: Also it's harder to debug a macro. It's already opaque to operate on functions because once you make a function it's opaque for analysis. And IMO macros push this non-analyzeability even further. So for me macros are special feature which I tend to not abuse. For instance I used a macro to make my own locking block for ReentrantLock or avoid try-catch of some sort.

Drew Verlee05:07:59

> "I don't need more languages." that's going to limit you on what problems you are going to be able to solve, and who you can solve problems for. I don't PL are generally the limiting factor here, it's platform they compile to. A hilarious example of this is written word, no mater what language you write a recipe in, it will never become a dish unless someone can read it and make that happen. Programming languages are a human interface, and racket isn't going to suddenly compile to an effective neural net without losing ever part of what makes it racket. I mean, someone would need to build the bridge so it compiled worked on a GPU.

1
Drew Verlee05:07:29

I'm probably not explaining that right and i'm probably talking outside my field of expertise. I'm not trying to diminish the ability to write a DSLs. Just trying to express that i'm not sure flexibility in expressiveness isn't the hardest problem for many tasks.

pppaul16:07:00

@U0DJ4T5U1 this is sorta the whole idea why you use language as an abstraction. this is exactly what tensor flow and other NN libraries are trying to do, elevating those tools to a language is just one more step in the abstraction layer. i find the replies to this thread really confusing, it's as if people are reluctant to view language as an abstraction tool, when clojure itself is an example of that very concept.

Drew Verlee17:07:30

@U0LAJQLQ1 I think the confusion is because it's not clear what the objective is. I'm mostly just chatting. I'm semi fluent in clojure so most of the functions i see in DSL are harder for me to read. Is there an argument to be made that, for example, people in the domain of designing "book covers" would be better served by using a DSL in racket then opening them up to all of Clojure? I would say so. It's just a different thing. So would building a webapplication for your book cover creator or connecting that to a database so people could save there designs. At what point do those integrations outweight the DSL? At some point maybe, it depends. it's a big pile of "it depends".

Drew Verlee17:07:50

For my part, i'm ignorant as to the nuance details in macros between Racket and Clojure, other then a vague sense that macros in Clojure are harder in part because of the JVM (is that right?). So it's good to be aware of that trade off. Put another way, if you don't need the JVM and it's accouterments, i'm not sure Clojure would be a good fit.

pppaul17:07:38

i don't consider rackets language abstraction tools macros, though they may be considered as some sort of sophisticated compiler macro. clojure has made specific restrictions for it's macros that are not related to the JVM at all. clojure didn't have reader macros at all until recently, it's not like the JVM changed to make that possible, and clojure is tagged to a fairly old JVM version. i would say that clojure as a concept is not tagged to the JVM at all anymore, janet, clojurescript, babashka, CLI, and probably some other implementations/derivatives have turned clojure into a much bigger thing. i'm starting to see babashka refs in cljc files now.

Drew Verlee17:07:51

So how are clojure macros restricted?

Drew Verlee17:07:04

I'm looking at the list of things that you say clojure would have trouble with and i'm having trouble seeing what the trouble would be.

Drew Verlee17:07:18

Unless the goal is to make clojure look like this DSL, in which case the problem isn't the output were trying to produce, but the basis of knowledge the user of the tools is starting from.

pppaul18:07:56

i thought the list of DSL projects made with racket was good enough. one example would be to let me label a file with a reader macro, or class of files, and let me use require/import/whatever in ns to read in that file based on it's reader macro. another limitation is that macros run at compile time, in other lisps there are both compile time and runtime macros. 3rd, in some languages functions and macros are the same thing, in clojure they aren't and you can't treat macros as if they are functions. I think there may be enough tooling in clojure to be able to implement the first idea, but the last 2 seem pretty off limits.

Drew Verlee19:07:05

And reading a file based on a reader macro is useful because it means the file can be written in a DSL that the reader understands?

teodorlu19:07:42

I think pollen is a nice example of something that racket "language oriented programming" is well suited for. Let's you mix code and documents. In clojure, I'd probably reach for a data representation. https://docs.racket-lang.org/pollen/quick-tour.html Edit: or see ppaul's previous link: https://beautifulracket.com/appendix/domain-specific-languages.html

pppaul19:07:07

@U0DJ4T5U1 just like regular reader macro functions, right?

pppaul19:07:48

i think the main difference from a regular reader macro would be including the file name in the macro form

pppaul19:07:14

pollen example is pretty easy to follow. i like how the reader macro tags get translated to functions so seamlessly

Drew Verlee20:07:36

I see. I don't know how i would add a pre-processor to markdown so that i could use variables in it. I would end up jumping to hiccup. Which means you have more control at the cost of having to manage that control. Which echos what the pollen author says. > If all you need to do is produce basic HTML, Markdown is fine. But if you need to do semantic markup or other kinds of custom markup, it’s https://docs.racket-lang.org/pollen/second-tutorial.html#%28part._the-case-against-markdown%29. ... > we need to convert our custom tags into valid HTML tags. At which point, I feel stronly that i'm looking at more or less the same thing hiccup is. Because the target is html. (https://docs.racket-lang.org/reference/define.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._define%29%29 items (default-tag-function https://docs.racket-lang.org/reference/quote.html#%28form._%28%28quote._~23~25kernel%29._quote%29%29ul)) <>items... vs (defn items :ul) [items ...] So here is the question. how do you add something to your list of <>items? can you conj it? Like this isn't me trying to poke holes in anyones battleship. I'm worried something is going right over my head here. It's the same thing when i talk to the haskell community about types. I feel like were just talking past each other and i just keep coming back to the core clojure datastructures are my bread and butter. Because they compose. Because computers have no idea what an "item" is, but they tend to all more or less agree on what a vector is (or at least all clojure programs do).

pppaul20:07:17

in the case of pollen, this doesn't look very far from regular templates, but much more integrated into the host language.

👀 1
Drew Verlee20:07:33

more integrated then hiccup is into clojure?

pppaul20:07:32

hiccup isn't a template, it's a data structure. you can imagine a template would make hiccup as it's output

Drew Verlee20:07:40

Yes, i can. why is that relevant?

Drew Verlee20:07:53

And thanks for chatting btw. 🙂 i have been curious about racket for a while, though it's not your responsibility to share anything about it. I appreciate the effort.

pppaul20:07:48

templates don't really do that. maybe you have access to their AST or some second class hooks into them, but not first class like pollen. also if you have ever dealt with ASTs, you can imagine not having to deal with them to be a nice feature.

Drew Verlee20:07:57

Ah, i see the more focused/restricted environment can be a benefit.

pppaul20:07:20

i would say that hiccup fits that exact description. that's why it's probably more popular than something like selmer. though for my projects i choose selmer because i want to work with non-clojure devs, even if it means more work for me to support them. i think something similar can be said for supporting other domains, (i've also done a lot of work with excel, because i thought it was the best choice for the people i worked with). i think i would be pretty nice to be able to work with something like selmer, but in the way pollen is set up.

Martynas Maciulevičius05:08:31

> reading a file based on a reader macro Why do we want to allow this? For instance alright, let's hypothetically consider adding(to clojure)/using this because this enhances the flexibility of the DSL that we could provide to our users. But what does it mean? Let's take this article as one possible outcome: https://www.scattered-thoughts.net/writing/against-sql This article says that even though there are obvious pros to use SQL there are some big downsides. For instance one downside is that some special syntax is not implemented on different SQL servers. What does it mean? It means that even if we have SQL standard there are many deviations and extensions of it because the thing that the standard describes is not good and doesn't fit everybody. There are so many syntax exceptions and anomalies that it quickly overwhelms the SQL compiler writer. We see that in Clojure already. For instance in CLJS we don't have agent reference type and in Babashka you can't import some libraries. On top of that the article states that you can't compose that SQL. And this is a very similar argument that Rich Hickey makes when he says that if you import multiple reader-changing macros you may end up with unusable system. So let's not end up with that even though your intentions are good. You can always write a macro that supports your syntax in the 80% the same way. And that's good enough. So probably what you could propose instead is some kind of reader-context that could allow to implement reader macros in safer way... (reader-context dependency injection library anybody?). I'm not sure why you care about having macros that are evaluated only during build time and only during runtime. Clojure already ships with its interpretator so you can call read and evaluate macro at runtime just fine (I didn't try it in all possible ways (looking at you, graalvm) but I think it should work) or you can simply call the macro when you connect onto your prod machine via REPL. And if you want to use runtime macros then why not use functions or functions that call macros? For me this sounds like you're tired of playing 3d chess (Clojure being LISP) because you saw this fun move in 4d chess (LISP with "better" macros and worse undefined behavior). Maybe it's useful to use once but why not make a library then and show that we need this for mainstream Clojure devs (Clojure is already kind of niche but yes, mainstream of "kind of niche"). Show us what cool stuff we could have and how crappy Clojure's syntax looks when we do it. If we can import core.async where people base whole languages around it (golang), we could have this too. Why not make seancorfield question his life decisions for not implementing it into Clojure sooner... Reader conditionals were implemented because of new hosts and they serve a very real purpose. And no, I'm not talking about files only - I talk about anything you don't like about. Maybe it's only one library away and you could discover that you can have a very good DSL even in Clojure. You could call it dslify<tm> or dsl-god. Also IMO as Racket is based on Scheme which is kind of focused on language research. So then it's the same question as not having Monads in Java. Also we could paraphrase the question and talk about opearators instead of macros. Why can't this + operation be usable on matrices by default? Why is default + so bad? Everyone needs complex numbers and matrix additions in all of their apps ever no matter what (actually Python sort-of does this, you can add a list to a list). Also I've read description about Racket and it said that Clojure is based on Rich Hickey's view of programming. But how can you have an objective view on programming? What is the "true" paradigm? What is the "real" and not "opinionated" language? Isn't having five macro types also an opinion on what should be possible? Is allowing to mix all previous language features a problem? Why not use foreach (syntax for(x:xs){}) and Stream in the same app in Java... foreach isn't deprecated or anything...

pppaul13:08:21

i'm not so sure why a reader macro operating on a file is so much different from on a string. i've written reader macros, and the main difference that comes to mind is file path, and newlines. also i did mention that clojure probably has enough tooling to implement what i described. the other things talked about are limitations of clojure, and your arguments seem like you haven't used languages that are free from those limitations. i'm not against limitations, i've used languages that don't have conditionals, or control flow, and i'm also happy using things like excel. i think that all the arguments supporting the limitations of clojure are supporting the idea of my initial statement, and go against the topic that started this thread "abstract anything". in clojure we don't do that, that's not the goal, and i was just pointing that out. also calling racket a research language just makes me think about when python was just a bash alternative, or when javascript was ugly and nobody wanted to touch it. there are real companies running racket, smalltalk, haskell, R, matlab, erlang, groovy, fortran, fouth, D, etc... you may be surprised how many companies are run on excel. Also, i don't see first class monads being absent from java in the near future, java did just recently add pattern matching, and here is the first article i found talking about monads in java, https://medium.com/thg-tech-blog/monad-design-pattern-in-java-3391d4095b3f. i don't think monads are very special, though, they are similar to OOP patterns.

Drew Verlee14:08:43

sounds reasonable. Racket would seem to better situated to build other languages, at the cost of a host of things. And the languages that Racket builds, I imagine, like any new language, have drawbacks. I could see Racket being a good for building a dsl around an already established highly precise domain language with a user-base that was invested enough to learn the tooling (as opposed to needing it pushed directly onto there phones and browsers).