Fork me on GitHub
Michael Fiano01:11:04

I'm desperately trying to get some feedback. My hobby programming life took a sharp turn when I switched to using Clojure a month ago. I was an indie game developer, using another Lisp exclusively for many, many years. A month ago I switched to Clojure after getting burnt out on gamedev, and all the OOP and mutability that went with that project, and so after watching about 10 Rich Hickey talks, and reading lots of documentation, I picked up Clojure a month ago, and successfully wrote 6 binary format parsers to get information on a few multimedia file types I have laying around on my file server. I got quite familiar with Clojure and Clojure->Java interop. It was fun, but very tedious, and I started losing interest in that project. As a programmer, I've always had writer's block. I'm just not sure of a project idea that excites me anymore. I really like Clojure, but I'm just not sure what I'd use it for. What's wrong with me? 😆


Do you happen to feel burned out with programming at work right now?


Because I've gotten that feeling where not only was I burned out with work tasks, but it bled over into my hobby time, because I felt like while I could start a hobby thing that might sound interesting, that hobby thing wouldn't become "interesting" until I had put so many hours into it, that it would be weeks or months before it became really interesting, and that was demotivating.

☝️ 12

How did you solve this problem? I’m sitting in that hole on and off and it seems to be a self perpetuating problem :)


Fortunately it has never lasted terribly long. I think in the few cases where it was fairly bad, I was able to take a 2-3 week vacation and read books for fun, and gained some perspective on the situation.


It was a scary thing, really, because when I was a teenager learning to program, I could spend all day with hobby things like that and never get tired or bored, so the idea of not being motivated by hobby things was "what is wrong with me?"

Michael Fiano02:11:07

I don't program at work. I am in the hardware industry.


Designing ASICs by any chance? Or some other area in hardware?


I only ask because I also did ASIC design and verification full time for about 5 years

Michael Fiano02:11:54

Nah, I'm a contractor for a few large computer hardware manufacturers. My day job involves driving for hours to get access to a network closet for 10 minutes to do a swap or rewire.


Regarding one thing you said above, I would suggest that perhaps using Clojure to do lots of Java interop might be a task that many people would find tedious.


i.e. you do it because you need access to a Java library for part of a project, and perhaps a significant part of the project, but it might not be what a lot of people would consider the fun part of the project.

Michael Fiano02:11:31

At any rate, I would like feedback on my first Clojure project. I probably broke all the rules of idiomatic code.


There is a #code-reviews channel (I haven't participated in it before, only heard about it) where you may find someone willing to dig in and provide feedback.

Michael Fiano02:11:07

Not that I'm very interested in continuing that project, except maybe in idle time.

Michael Fiano02:11:29

I'm trying to decide what I want to do for hobby programming. The Clojure gamedev ecosystem is pretty lacking. I'd need to write a few big projects that would take me at least a couple years just to get back to where I was. I'm not sure if that's what I want to do yet.


I know I have heard of some people doing game dev in Clojure, that may have some libs you could look into, but agreed that it isn't the most common area that people apply Clojure to.

Michael Fiano02:11:02

Yeah, the most difficult thing I would need is a compiler for Clojure to GLSL, so I can again have live coding of GPU code, which is a huge task. This currently only exists partially for GLES, which is the stripped down mobile/web GL version.


I am just doing 'rough pattern matching' here, so maybe not useful, but Dragan (forget last name) has been using Clojure to program GPUs in pretty significant sounding ways, although I don't know if his work is applicable to game dev or only other applications of GPUs.

Michael Fiano02:11:06

His use-case is more ML and statistics programming. Nothing really useful for game development, and especially deployment of games, since neanderthal (his linear algebra library) requires foreign libraries to operate, which would be passed to my users with deployment in the form of 2.5GB for intel-mkl for example.

Michael Fiano02:11:50

What I need is a Clojure DSL that compiles to GLSL code


I have no idea if there are Java libs for what you want, or whether they would be interesting/fun to drive from Clojure programs, but the main reason Clojure is hosted on the JVM is to have access to the large collection of Java libs.


not something i have heard of, so i could not help any more than Google can there

Michael Fiano02:11:21

Graphics programming is sort of my thing. I like experimenting with graphics on the GPU and all the math involved.

Michael Fiano02:11:55

The benefit of a GLSL compiler is plentiful, but basically comes down to 1) I don't have to write most of my code in a C-like language, 2) I can live code, and 3) I can compose and reuse functions, rather than load whole strings of GLSL programs consisting of many functions in.


Have you checked project? It’s a collection of clojure/script libraries for designers. It has a geometry module for webgl graphics, although there’s no glsl compiler yet.

Michael Fiano02:11:56

I may try, even though it's quite a feat. I was spoiled by the couple options available for this in Common Lisp


Not sure if you are interested in perhaps learning from those CL implementations and reimplementing them?


This might sound heretical here. I've found programming in a statically typed language with IDE that does real time type makes even the most boring of work tolerable.


The IDE/Compiler is constantly telling me what is wrong / what I need to fix next, so it's quire easy to get into flow.


One needs to consider is that you may not write the same sort of code in both languages. The Clojure version and the say Java version may lie in different places in the genericity/specificity spectrum, with different tradeoffs. Specific code makes it easier to relate an error to its cause, but is less reusable - Clojure code tends to be very generic. For instance, a function is more reusable if it assumes its input is a dynamic map than if it's a Java object with getters and setters; on the other hand, that function may not give you a very precise error message when a key is missing in the map.


This is less so with a language like Clojure, where balanced ()'s = syntactically valid. Then, if a unit test fails, there's mental thought required to figure out what went wrong instead of just clicking on the error msg and jumping to the right file/line.


Also, are there new mac mini's available for $499 anywhere? I thought that used to be the price, but current mac mini's seem to start at $799 ... which is quite a jump.


@todo I'm currently working on a Rx Java application. The support you get from the idea is pretty limited. IntelliJ does show the type of the observable, which is nice. But not whether it's a single sumitter or if it can be empty. Also the boiler plating makes it harder to read.


But I do get your point, for small hobby projects in Java, Kotlin or Rust the code is most of the time working when it compiles, but clojure(script) not so much, but that also has to de with my limited use of the repl during development.


I never worked on a large clojure(script) project, or on written by someone else for that matter. But for those things types are nice. With clojure it should be a lot less code. But what's easier to read, 5000 lines of typed code, of 1000 lines of untyped code, I don't know.


I've found 1000 line of Clojure easier to read then 5000 lines of typed code.


It also depends on the writer you could put everything in one file, use imports reducing them to single letter imports, use non saying names for your own functions and it becomes unreadable.


(* (|< W &*) (T (-> (do (Y_) (O_) (U_)) (mean)))))


Unreadable maybe, but it can still be understandable. There's a few things in Clojure that tend to make code simple. The syntax is uniform and consistent. The data is immutable. The code executes from top to bottom. The development environment is interactive. This makes for code structure that tends to be easy to reason about and underhand, even without types or proper names.


@U3JH98J4R That's not valid Clojure. Most of those symbols don't exist.


But ya, bad Clojure code vs amazing Java code, maybe I'd rather work with the Java codebase. But bad of each, I might still prefer Clojure. And good of each I'll still prefer Clojure


I can make it valid clojure though with some macros or let


its not valid clojure in the same way (+ a b) isn't valid


Sure, but then it wouldn't be that hard to understand


Because the syntax is consistent, the program flow isn't that hard to follow. We just don't know what value each symbol maps to, forcing us to go look at each definition for each one. The question is more, would types help? I don't think so.

(let [x "hello" b 10 c [1 2] d -1]
  (-> x 
        (f b)
        (g c d))
String x = "hello";
int b = 10;
List<int> c = List(1, 2);
int d = -1;
String u = f(x b);
g(u c d);
I don't find the types really help me. The challenge is the same in both, I need to go look at the implementation of f and g to figure out what they could possibly do. I think what people struggle with in Clojure when it comes to reasonability isn't actually the lack of types, but the lack of schema. Like what are the keys in this map? Indirectly, typed language tend to enforce schemas, such as the use of custom composite types, like a Class with a fixed set of fields. Though you could very much use a HashMap for it all and suffer from the same issue that you do in Clojure. Clojure also developed mechanisms to help with that. Mainly Records and Spec. Preconditions to some extent.


> its not valid clojure in the same way (+ a b) isn't valid Its different. I know what + does. So I know this adds a and b together. In your example I don't know what T and U_ etc. do. Maybe U_ shuts down my computer for all I know :p


One needs to consider is that you may not write the same sort of code in both languages. The Clojure version and the say Java version may lie in different places in the genericity/specificity spectrum, with different tradeoffs. Specific code makes it easier to relate an error to its cause, but is less reusable - Clojure code tends to be very generic. For instance, a function is more reusable if it assumes its input is a dynamic map than if it's a Java object with getters and setters; on the other hand, that function may not give you a very precise error message when a key is missing in the map.


@gklijs: I have used Kotlin, but not Java in IntelliJ -- and found it a joy to work with. Kotlin seems to have eliminated most of Java's verbosity, and the rela time auto complete / type feedback in Kotlin was amazing.


Regular Java works fine, and in the absence of Kotlin data classes you can easily generate getters/setters or use Lombok. But there are limited to what the type checker can do. IntelliJ also goes a bit further then that, and also hints for possible nullpointer exceptions. We also have sonar hooked in to further reduce issues.


@mfiano way out of my domain, but @sekao has done a bunch of game dev on Clojure (and given a very nice talk on this at some Clojure/conj). Also, Ramsey Nasser ( is in this space, but mainly in ClojureCLR.

Daniel Hines14:11:39

Re: REST vs GraphQL. Is it fair to say REST, the way it’s typically implemented, is essentially an ORM layer? Running with that, Rich Hickey talks about how Datomic obviates an ORM layer. Is the quality that GraphQL, Datalog, and Pull share, namely allowing consumers to format data as they require, what Rich is referring to when he says that?


GraphQL though doesn’t allow arbitrary joins or queries. It’s a very common misconception, probably because of the misleading name.


You have to upfront specify your data’s schema, using a type system, then explicitly define what queries can be run against it.


Datalog is a relational algebra, much more powerful, and also dangerous to expose directly to the browser.


In some sense, GraphQL is exactly an ORM - though how you implement it is up to you, so there isn’t necessarily a relational layer underneath.

Daniel Hines15:11:26

From Simple Made Easy: “”ORM is complex, and declarative data manipulation is simpler” Isn’t the whole point of ORM to encapsulate the retrieval and manipulation of your data, and thus decomplect it from how it’s stored?


The objective of an ORM is more than just that, it's also to expose an Object-Oriented interface to querying the data (via method calls). This approach often fails to acknowledge the distributed systems reality of querying the data as well as its representation, and that's where a lot of the complexity comes from. (and arguably also because OO code is a weaker query language than the declarative alternatives like SQL or Datalog or to some extent GraphQL)


Providing a database-agnostic interface is not ORM (since it's not about objects), it's just a database client library


ORM tries to map the relational data model, with the OOP data model. Mainly, how do you map between a model where all you have are flat tables with no nesting. And a model where you have single named records with arbitrary nesting.


Think about it. A table is a list of lists. [["Age" "weight" "size"] [1 2 3] [3 4 5]] Where elements can only be primitives. You can't nest another table, or another collection inside.


But an object in oop is a fixed key record. Like {"Age" 1 "weight" 2 "size" 3}. Except unlike Clojure maps, the keys are fixed. You can't add more or remove existing ones.


The first problem is, an object can only represent a single table row + header row.


So how do you map a whole table, consisting of a header row + zero or more data rows? To an object?


The second problem is table relations are implicit. But in OOP, they're explicit, using nested data.


[[id name] [1 "john"]] [[id item] [1 "xbox"] [1 "controller"]]


So now in OOP, you might do: {id 1 name "john" items [{id 1 item "xbox"} {id 2 item "controller"}]}


And as you continue that path, things get quite complicated, and you realize the two data models are pretty different and don't always have equivalence.


Thus some people say, ORM are too complicated, and create an unneeded overhead. Just write code that uses the relational model as well, and avoid all the complications related to trying to map back and forth.


Now the part about declarative vs imperative I believe it's because you have to tell the ORM how to map the tables to your objects and back. And once you've got the data as objects, you can no longer use a declarative query language over it.


ORMs traditionally have tried to abstract away the SQL/relational reality of data, and convert them into Objects with a fixed layout.


There is a new flavor of ORMs that just help you write composable SQL though.


I will stay with my initial definition that GraphQL is about defining the data format


As a client you have the guarantee that the JSON you receive in response will be shaped like your query, respecting non-null constraints and scalar content constraints


And that’s about it


If you ask for: { heroes(name:”Luke”,era:”modern”) { name } }


(Excuse the crap formatting, I’m on the phone)

3Jane22:11:58 have no guarantee whether that gives you a list of heroes named Luke AND living in the modern era, or a list of heroes EITHER names Luke OR living in modern era (or both). (Or it could ignore name because era overrides it. Or it could return null everywhere because your IP doesn’t have access to personal data of heroes. Or... )


Because this is up to the server implementation (and of course should be documented, and you should have a consistent convention in your type system)


In SQL you define WHAT you will fetch (select, conditions) and also HOW you will fetch it (joins, also conditions, sometimes even hints) at the same time


In SQL I have to care whether I use WHERE x IN (1,2,3) or WHERE x IN SELECT VALUES (1,2,3)


(Can’t remember the syntax of the latter of the top of my head but look it up - for larger lists of values the latter is faster in Postgres because of how the IN is translated internally)


But in GraphQL, when I send a query I don’t know anything about internal performance concerns, the order of data fetching, joins, indices, data sources, or anything else of that sort. And I shouldn’t! I don’t want to know! (which is why I’m a big opponent of the services and libraries “automatically generating GraphQL types from your DB” - talk about shooting yourself in the foot!)

💯 4

Rant over for today XD


Instead of trying to compare it to what you already know, play with it a bit and you’ll grok it much better. I think it does make things simplER, although it has its warts as well.