beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Joel 2020-09-11T05:41:00.095600Z

How do I sort a list based on an ordered sequence. For example [:low :medium :high], I want my list sorted so lows are first highs are last.

2020-09-11T05:48:42.096700Z

You turn the vector into a map of element to index, then sort-by with that map

🀘 1
simongray 2020-09-11T10:37:49.097200Z

clojure can be so elegant in its simplicity.

Joel 2020-09-11T05:52:31.097100Z

ah so { :low 1 :medium 2 :high 3 }

βœ… 1
Grigory Shepelev 2020-09-11T12:53:31.098300Z

What the problem with my standalone jar?

Error: Could not find or load main class <http://genomics.app|genomics.app>
Caused by: java.lang.ClassNotFoundException: <http://genomics.app|genomics.app>
I'm a total newbie in devops-related stuff on java/clojure interop and that's my first uberjar attempt. Sorry for the questions that might be trivial

alexmiller 2020-09-11T12:56:05.099100Z

the message is telling you that the jar does not contain genomics/app.class

alexmiller 2020-09-11T12:56:34.099700Z

you can see inside the jar with jar tf whatever.jar - probably good to see what it includes

alexmiller 2020-09-11T12:56:56.100100Z

you might need to add (:gen-class) to that namespace and aot compile it

Grigory Shepelev 2020-09-11T13:00:42.101Z

Yes. Guess that's the problem. Where should I put it?

(ns <http://genomics.app|genomics.app>
  (:require [genomics.gui :as gui]
            [cljfx.api :as fx]))

Grigory Shepelev 2020-09-11T13:00:59.101600Z

before :require?

Grigory Shepelev 2020-09-11T13:12:37.102200Z

I added :gen-class but it still shows the same exception

alexmiller 2020-09-11T13:20:40.102400Z

can be either before or after require

alexmiller 2020-09-11T13:20:48.102700Z

and then you need to make sure you aot compile the code

alexmiller 2020-09-11T13:21:05.103100Z

if you're using leiningen, adding :aot :all should do it

practicalli-john 2020-09-11T13:49:04.103300Z

Just add :gen-class by itself if you are only calling http://genomics.app functions from the command line

(ns <http://genomics.app|genomics.app>
  (:gen-class)
  (:require [genomics.gui :as gui]
            [cljfx.api :as fx]))
I do this with the clojure apps I run, e.g https://practicalli.github.io/clojure-webapps/projects/banking-on-clojure/application-server-configuration.html

Grigory Shepelev 2020-09-11T14:00:49.107500Z

It's not helping. Lein just hangs. It's probably libriary-specific problem. I asked in #cljfx but still no answer

alexmiller 2020-09-11T14:02:49.109800Z

if it's hanging, what's probably happening is that you have top-level defs in your code that are being run during compilation

alexmiller 2020-09-11T14:02:59.110Z

which is generally a bad idea

alexmiller 2020-09-11T14:03:15.110300Z

but that's what I'd look for

Grigory Shepelev 2020-09-11T14:03:39.111300Z

I don't know what's ment by "top-level expression". Why it runs in lein run?

Grigory Shepelev 2020-09-11T14:03:42.111600Z

perfectly

alpox 2020-09-11T14:04:11.112100Z

Hi all! There is a rather small Website I wanted to try Clojure on but got stuck early. I use the Luminus Template with HugSQL for it - the database behind is (must be) MySQL I will need all text of the entities to be multi-lingual and wondered what would be the best way to treat it with HugSQL. One way would be to write out the table-spanning multi-lingual queries for every entity but that would kind of be a huge verbose bunch of SQL. I wondered if someone already made a good setup / has some ideas for how to structure those queries with HugSQL. I'm using a single translation table together with a single language table but I'd be happy to change the table layout if it makes the usage better

alexmiller 2020-09-11T14:04:23.112300Z

compilation of ":all" may not happen in the same load order as your app

alexmiller 2020-09-11T14:04:38.112500Z

I'm flying blind here, just making my best guess

2020-09-11T16:58:56.116800Z

Hey team, curious question for you: What is clojure’s thought on β€œdata abstraction”, as in: say you use a map to represent some data-structure β€” user You would use functions to operate on the user data structure like (name user) (address user) , rather then keying directly: (:name user). The main reason being: could change the underlying implementation of user (i.e from map to list or something) (I see this pattern a lot in the older lisps: norvig talked about it paradigms of ai programming, and sicp mentions it) I see the benefit, but at least for clojure some of these function seem unnecessary. What is the clojurists way when it comes to this?

2020-09-11T17:02:20.117Z

sometime it is handy to think about data structure (maps to be precise) as about functions (let [user {:name "Foo" :address "address"}] (user :name))

alexmiller 2020-09-11T17:17:19.118Z

the whole benefit of maps and being able to use keywords as functions directly is that we don't need to make another layer, so my pov is that that kind of "abstraction" is not abstraction, it's indirection and it's downsides are bigger than its benefits

❀️ 3
alexmiller 2020-09-11T17:17:53.118400Z

don't make functions that duplicate what you already have

πŸ‘ 3
seancorfield 2020-09-11T17:20:39.119800Z

We've used it at work only where we are migrating from mixed case keys to lowercase keys or to namespace-qualified keys and some code has to be able to hand "both" while we are transitioning, but I would not recommend it as a normal mode of operation.

πŸ‘ 1
πŸ‘€ 3
nick 2020-09-11T21:01:55.120400Z

Is there a specific need why those "entities" need to be stored in MySQL? Perhaps it all can be stored in a simple .edn file? It can be updated by non-techy people, it is easy to validate its form and switch between languages.

2020-09-11T22:20:09.123300Z

The distinction that Clojure makes between a string β€” "A" β€” and a literal character β€” \A β€” is pretty awesome. This seems useful.

Mno 2020-09-16T11:09:16.281100Z

It sure is

2020-09-11T22:21:48.124700Z

@stopachka The function (:foo m) is already a data abstraction, because it is implemented not for PersistentMap, but for anything that implements ILookup. And Associative implements ILookup, and that's also an abstraction which PersistentMap implements. So you have 2 layers of data asbtraction happening when you call (:foo m) already πŸ˜›

πŸ‘ 1
πŸ’― 1
2020-09-11T22:26:23.125400Z

Gotcha, thanks for the clarity folks!

2020-09-11T22:28:40.127Z

Ya, it means in theory, you could create a new data-structure and have keyword as function work on them to retrieve some associated value. Though since Clojure is implemented in terms of Java interfaces and not Clojure Protocol, you cannot extend it yourself, you need to modify the source code of the data-structure itself, so you can't for example add support for it to PersistentLists or sets or strings, etc.

2020-09-11T22:37:43.131100Z

https://github.com/arohner/clj-wallhack/blob/master/src/wall/hack.clj#L38-L55 is an example of a custom ILookup

alpox 2020-09-11T22:41:14.133500Z

The reason for the translations to be stored in MySQL is for that they can be edited through the website. An edn would be possiblw if the backend would be hosted with a persistent storage attached which for this project wont be the case

2020-09-11T22:49:52.139900Z

But I think fundamentally, what you're reading about data abstraction has some other concept in it which is kind of different, which implies creating custom user defined types, such as a User type. This gets complicated, and I don't know if I should venture about this in "beginner" but here goes πŸ˜› {:name "John" :email "<mailto:john@gmail.com|john@gmail.com>"} is a Map which contains information about a user. We did not create a "User" abstraction here. We just put information about some user in an existing reusable abstraction called a Map. This means that if you want to manipulate the information about the user, you can use all the existing Map functions to do so. You do not need any new functions. This is a common pattern in Clojure. We commit to the way we decided to represent information and just choose to use this directly. It does mean that if you decide to represent the information in a different way later, you break all usage of it. So if you change :name to :first-name you broke a bunch of code. Similarly, if you choose to change the model to a list like so: [:user "John" :email "<mailto:john@gmail.com|john@gmail.com>"] you also broke all the code that was using the map directly. You can decide yourself if you mind this possibility or don't care. I think most people in Clojure wouldn't care, because in practice, you almost never change the structure πŸ˜› So you can think of it as, maybe it takes you a little longer to refactor in the rare case you would need to change it, but you saved yourself a bunch of time not bothering with some half-baked User abstraction that most of the time will just be more inconvenient to work with than a Map/List/Set would be. In a language with mutable data-structures this would be a huge risk! Because you need to protect the usage of the data from uncoordinated concurrent modification. So when data is mutable, you would definitely want to protect access behind a function. And Clojure enforces that if you create a deftype for example, which provide mutable structures, like it forces you to always have data accessors and prevents you from changing the mutable data directly. Even with simple structure, like a single mutable field, Clojure forces you to use some data abstraction to change it like with atom where you can only do atomic changes to it.

2020-09-11T23:03:37.140Z

Ya, I remember at some point I was thinking, should I put all my keywords behind a Var. Like:

(def name :name)
And then do (name map), just so I could change this easily if the key changed. But never bothered with it lol. I feel its only a problem the one time where you are like, damn we have a typo in the key, I'm OCD and want to fix the typo.

2020-09-11T23:07:02.140400Z

It would be pretty cool though if ILookup could be extended by metadata. Then you could like change your Map or whatever and just add some meta adapter for :old to :new

2020-09-11T23:09:11.140700Z

Hum... or I guess in theory, this might get funky lol. But what if you could have a self-referential map where the key: :old pointed to the value of the key :new in the same map :thinking_face: