Fork me on GitHub
#clojure
<
2024-04-13
>
mmer10:04:56

Is there somewhere that explains how to create a library using clojure?

p-himik10:04:47

If it's just Clojure, without Java and without anything else that requires additional steps, then the bare minimum required to create a library is to publish the source code on some Git service with a deps.edn file at the root. Such a library can then easily be used by providing Git coords. Other steps depend on what exactly you're doing, what you want to achieve. Want to publish it to Clojars? There are articles about it and tools for it. Want to intermix some Java code? You'll have to use the step above as well, and also use the right compilation procedure. Need some preparation steps that must be run by the users of the lib? I think tools.deps guide has something about it.

1
teodorlu11:04:23

Getting started if you publish to github is quick. All you need is a deps.edn file and a source file:

$ echo '{}' > deps.edn
$ mkdir -p src/mmer
$ echo '(ns mmer.mylib) (def x 42)' > src/mmer/mylib.clj
If you publish that to github, others can use your library by adding
io.github.mmer/mylib {:git/sha "GIT SHA"}
to their deps.edn file. Then people can import mmer.mylib and use mmer.mylib/x.

seancorfield16:04:55

@U4C3ZU6KX You might find this extended example of writing, building, and deploying a library helpful: https://clojure-doc.org/articles/ecosystem/libraries_authoring/

💯 1
mmer08:04:38

Thanks everyone.

Jim Newton14:04:54

I will be teaching a techniques in functional programming class starting in May--I'll be using clojure as the language of choice. Students have already seen Common Lisp (but they are not experts) as well as OCaml (2 years ago) and Haskell (again, they are not experts) and some students will be taking a Scala elective in parallel. I still have not decided which editor/ide to recommend to my students. Someone recently mentioned a supposedly easy and light weight editor which I'd never heard of. I don't see it on the page https://clojure.org/guides/editors nor on https://clojure.org/community/tools Personally I use emacs, but I've been using emacs since 1985. I don't think my students will find it to be a good recommendation. I notice that many of my students use vim, and the editors page mentions fireplace and vimiced. Since I'm not a vim user, I cannot say whether those recommendations are good or bad. Also some students use VScode, but i've never used Calva nor vscodespace. I'm open to anyone's point of view.

p-himik14:04:40

Maybe this is that editor? https://github.com/tonsky/Clojure-Sublimed Upd: ah, it's not on the first page, but on the second that you've mentioned.

p-himik14:04:02

Also, while I'm not a teacher, I myself would definitely go with something that's relatively easy, popular, cross-platform, flexible. I don't know enough about Sublime to say whether it ticks all the boxes, but VSCode definitely does. As a bonus, the docs and the beginner's REPL are great. And if there are advanced students, it might make sense to allow them to use whatever they want. With a caveat that if they encounter any problems with their setup, they're pretty much on their own.

Jim Newton14:04:21

strange. I didn't even recall that I had a file ~/.clojure/deps.edn, but it does not seem to have errors in it.

p-himik14:04:17

Maybe you meant to post in the main channel? Are you using the latest CLI version?

jgomez14:04:46

If I was teaching a class I’d suggest VSCode too, it’s cross platform, simple, easy to setup, and out of the box comes with LSP support etc. Disclaimer: I am a NeoVim user.

Jim Newton14:04:34

is cli something I installed with brew install

Jim Newton14:04:56

[Geminiani:~] jimka% clj --version
Clojure CLI version 1.11.1.1386
[Geminiani:~] jimka% 

p-himik14:04:12

Works on my end with 1.11.1.1435.

p-himik14:04:41

> is cli something I installed with brew install Maybe, maybe not - depends on your system and preferences. In any case, all the possible officially supported instructions are here: https://clojure.org/guides/install_clojure

p-himik14:04:01

I myself avoid brew like a plague, if I can help it.

Jim Newton14:04:32

brew doesnt seem to know about clj

p-himik14:04:24

What do you mean?

Jim Newton14:04:07

trying brew upgrade clojure

p-himik14:04:44

Please check the page I've linked.

Jim Newton14:04:48

yes, on that

Jim Newton14:04:17

seems to be a cascade of dependencies.

Jim Newton14:04:29

including qt

Jim Newton14:04:21

why's it installing guile?

p-himik14:04:33

Because that's what brew does. :D And one of the reasons I avoid it. Just to double-check - are you using the clojure/tools/clojure tap?

Jim Newton14:04:56

seems that brew upgrade brew upgrade clojure/tools/clojure successfully upgraded to

[Geminiani:~] jimka% clj --version
Clojure CLI version 1.11.2.1446
[Geminiani:~] jimka% 

Jim Newton14:04:17

still fails

clj -X clojure.core.server/start-server :name repl :port 5555 :accept clojure.core.server/repl :server-daemon false
Error building classpath. Error reading edn. Map literal must contain an even number of forms (/Users/jimka/.clojure/deps.edn)
[Geminiani:~] jimka% 

p-himik14:04:32

What is your /Users/jimka/.clojure/deps.edn?

Jim Newton14:04:42

;; The deps.edn file describes the information needed to build a classpath.
;;
;; When using the `clojure` or `clj` script, there are several deps.edn files
;; that are combined:
;; - install-level
;; - user level (this file)
;; - project level (current directory when invoked)
;;
;; For all attributes other than :paths, these config files are merged left to right.
;; Only the last :paths is kept and others are dropped.

{
  ;; Paths
  ;;   Directories in the current project to include in the classpath

  ;; :paths ["src"]

  ;; External dependencies
 
  ;; :deps {
  ;;   org.clojure/clojure {:mvn/version "1.10.0"}
  ;; }

  ;; Aliases
	;;   resolve-deps aliases (-R) affect dependency resolution, options:
	;;     :extra-deps - specifies extra deps to add to :deps
	;;     :override-deps - specifies a coordinate to use instead of that in :deps
	;;     :default-deps - specifies a coordinate to use for a lib if one isn't found
	;;   make-classpath aliases (-C) affect the classpath generation, options:
	;;     :extra-paths - vector of additional paths to add to the classpath
	;;     :classpath-overrides - map of lib to path that overrides the result of resolving deps

  :aliases {
            {:outdated {;; Note that it is `:deps`, not `:extra-deps`
                        :deps {com.github.liquidz/antq {:mvn/version "RELEASE"}}
                        :main-opts ["-m" "antq.core"]}}
            }
  ;;   :deps {:extra-deps {org.clojure/tools.deps.alpha {:mvn/version "0.6.480"}}}
  ;;   :test {:extra-paths ["test"]}


  ;; Provider attributes

  ;; :mvn/repos {
  ;;   "central" {:url ""}
  ;;   "clojars" {:url ""}
  ;; }
}

p-himik14:04:13

Your :aliases map is borked.

Jim Newton14:04:06

not even sure what that is. i seem to recall trying to do somdthing with antq at some point in the past.

p-himik14:04:29

You can remove the whole thing if you don't need it. But it has an extra {...} around it.

teodorlu14:04:40

I've recommended Calva to all people I've introduced to Clojure for the last year, and I've had generally good results. I also use Emacs myself, but I find that a good Emacs setup takes more effort than I think is reasonable to spend for a beginner who wants to learn Clojure (not Emacs). I haven't used other clojure editors than Emacs+cider and vscode+Calva in anger, so I have any opinions on those.

Jim Newton14:04:02

can deps.end be empty or can I just remove the file?

Jim Newton14:04:43

I emptied the file. clj is thinking a long time.

p-himik14:04:11

You can remove it altogether. Not sure about an empty file, I think it should work.

p-himik14:04:42

clj is thinking a long time.The command you're running won't exit because of the parameters you have passed it. What are you trying to achieve anyway?

Jim Newton15:04:37

I guess I'm confused. you send me a link: https://github.com/tonsky/Clojure-Sublimed What am I supposed to do with that?

Jim Newton15:04:49

ahhh is sublime a text editor?

Jim Newton15:04:19

i misunderstood. I though clojure-sublimed was a clojure-only text editor which would be lauched by running the code in the link.

p-himik15:04:24

Just as the README describes, the command starts only the socket server. The :server-daemon false part prevents it from exiting immediately. It resides in the foreground and listens for connections on the specified port.

p-himik15:04:16

To be clear - the link that I sent did not mean to show that I recommend that plugin for Sublime. I don't use that editor, I have zero clue about it.

Jim Newton15:04:04

ahh, it wasn't clear to me that sublime was an editor.

Jim Newton15:04:55

perhaps Package Control: Install Package is something inside sublime. I thought that running that clj command would start some environment which would allow users to edit clojure code. i was too optimistic.

p-himik15:04:15

If you want to make things easy to get started with, check out https://calva.io/get-started-with-clojure/ But note that it's just "for a taste". It doesn't install anything locally, everything happens in the browser. So a proper class should probably rely on a proper locally installed IDE.

pez15:04:39

I would recommend Calva, but I’m biased. 😀 (Calva maintainer here.) We are most happy to help you with checking it out and preparing instructions for your students. Feel welcome to ask for help in the #CBE668G4R channel.

seancorfield16:04:30

As a fairly long-time Clojure user (since 2010) who has used pretty much every editor at some point with Clojure -- including Emacs, since I'd been using that back in the '80s and '90s -- and I'll put in a "vote" for VS Code for students getting started. Since they've already used Common Lisp, they shouldn't be put off by the defaults that try to help you keep parens and brackets balanced etc. @U06BV1HCH is also teaching students Clojure -- but his students are very much beginners in programming (as I understand it) so he wants to avoid any paren-balancing or restrictions on copy'n'paste so he starts by having them disable several defaults in VS Code.

lspector17:04:53

@U04V70XH6 some of my students are indeed beginning programmers, but more to the point many are not necessarily on a path to becoming software engineers or otherwise motivated to learn new ways of typing and editing text in order to code in Clojure, even if they have already been coding for a while in other languages for one reason or another. They might be musicians, artists, biologists, or who-knows-what-else for whom Clojure is a great language for something they want to do, but for whom having to learn a new way to type/edit is just an unnecessary hurdle and needlessly intimidating. Probably some of them (like me, a 40-year Lisper, who has been teaching students Lisp for something like 32 of those years) also just think and type and code in non-linear ways, and find an editor that thinks it knows better than they do to be a nuisance and something they'd rather not have to fight with. I know that many in the Clojure community think that auto-typing closing brackets and even strict structural editing are awesome and essential, but I don't, and I know that many of my students don't. I think there are probably a lot more like me who are much less likely to make their voices heard in a Clojure forum. Particularly members of underrepresented groups who are already intimidated by many aspects of Clojure. Knowing how to type and edit is maybe the one bit of solid ground on which they can rely; if you deny them this, you're telling them pretty loudly that they don't belong. BTW I do think it is essential to have two key features that "try to hep you keep parens and brackets balanced": bracket matching and re-indentation. These show how to balance brackets and they show the structure of code, but they don't force you to type in any particular way, or prevent you from cutting and pasting like you've done for your whole digital life in every other text editing context. Helping (by showing what matches what, and by showing the structure of code through re-indentation) is wonderful and I would agree essential, but forcing particular new ways of typing, interfering with ordinary typing (for example by adding a ")" where you don't want it when you add a "(", and then making you delete it -- if it even lets you), or preventing ordinary cutting and pasting if the cut/pasted text isn't balanced is, IMNSHO, counter-productive for many users, including both some beginners and some old-timers like me. I'm here to say that I think that this is counter-productive even though almost every hard-core Clojure developer seems to find that unfathomable.

seancorfield18:04:37

Thanks @U06BV1HCH -- I figured your background and experience would be useful information for @U010VP3UY9X since he seems to be coming from a similar place (many years of Lisp experience) even if his students aren't in the same cohort as yours. "try to help you keep parens and brackets balanced" -- that phrase was deliberately doing a lot of heavy lifting in my comment while also being deliberately ambiguous 🙂

👍 1
1
lspector21:04:56

@U010VP3UY9X I can also confirm that Calva in VS Code is pretty easy for students to set up and use, and I have taught with it recently with reasonable success. There is also great support for it here, in the #CBE668G4R channel. I have also taught in the last few years using Cursive, which is built in IntelliJ. I moved from Cursive to Calva mainly because it assumes less expertise on the part of users an has less incidental complexity. Maybe there are fewer power-user features? I don't know, but it seems like there is somewhat less that can go wrong and cause confusion. All of that said, I am super excited that the beautifully-minimal Clooj IDE that I used many years ago looks like it will soon have a new life. Check the #C06T200ULK1 channel for some details. Assuming this is packaged up for easier installation (as it was in the past) by the time I teach my next course I plan to start with Clooj and allow students to stick with it if they want, or to move to Calva if/when they are ready to do so. In case it helps to assess the relevance of my experience most of it has been teaching undergrads at US liberal arts colleges, with a broad mix of students including computer science majors but also others. I myself am an AI researcher and cognitive scientist (doing a lot of my work in one Lisp or another since the 80s, including work on a variety of Lisp machines back in the day) and most of my courses that use Clojure involve AI or machine learning. These courses usually start with a crash course in Clojure which also usually involves a crash course in functional programming. After a few weeks most students are ready to begin using Clojure to code up AI/ML ideas that are the real focus of the course. They mostly use pure Clojure without any external libraries although some will eventually do project work that involves more. I'd be very happy to discuss this more here or by email (<mailto:[email protected]|[email protected]>).

mogverse16:04:27

In your Clojure applications that are sufficiently complex, do you feel the need to have a standard app level schema for a business entity and convert from/to this form when performing IO with external world (DBs, caches, streams, external APIs etc)? Quite often I have noticed, specially if the DB itself doesn't impose a schema, (like Mongo), the application's idea of shape of the entity assumes it to be the same as it is in the DB. And if you have to change the DB, it becomes hard to change all the raw key access from the {}. Worse if you're dealing with a Kafka stream payload or an Elastic search document representation of the same entity as well. I can think of using Protocols and Records, and go the OOP way. Or at least have data accessor functions on top of these maps, and kind of treat them as getters from the OOP world. While I like the idea of free maps, I feel if they're restricted within some bounds (namespaces), it is a lot more manageable. How do you approach this problem?

p-himik16:04:47

It's a common suggestion to have specs at the boundaries of your systems. And your DB in particular sounds like it's not really a part of the system that you control but rather an independent component, so I'd treat it as a boundary as well.

ghaskins16:04:00

We tend to encode most things as protobufs so there is a common representation between the wire (GRPCs) and persistence (DB, Kafka). We use protojure so the in-code representation is clojure maps

ghaskins16:04:18

(disclosure: I am the protojure author, so theres possibly some bias there)

ghaskins16:04:15

but the bottom line is we use .proto schema and evolution

seancorfield18:04:25

Where we feel it is necessary, we will add a transform layer so that isolates code that needs to change if the DB schema or APIs change. In the general case, that means one or more input formats (that might well have Spec descriptions), a single internal "domain" format, and one or more output formats (that might also have Spec descriptions). It depends on how much you expect an input or output format to change and how complex the domain logic needs to be. The more complex it is, the more you need a stable format and the more likely you might be to add those input/output transforms.

gratitude 1
seancorfield18:04:29

(so there are definitely places where we consider the DB schema -- which we control -- to be stable enough and close enough to the domain model that no transform is needed)

👍 2
mogverse18:04:23

@ghaskins Got it. Interesting approach. When you store data in a DB as protobuffs, I am guessing that's just then main data, you keep indexed fields outside of it also. I am picturing a user data in DB like this (single row or document):

{id: <uuid>,
 email: <email>,
 username:<username>,
 payload: <protobuff>}

ghaskins18:04:07

thats exactly right

ghaskins18:04:39

the db only contains fields that explicitly need to be indexed…everything else is serialized (and typically encrypted) in a payload blob

ghaskins18:04:49

(well, indexed fields, and any ancillary fields like MVCC collision mechanisms

ghaskins18:04:55

shards, etc

mogverse18:04:17

So in short, anything that the DB needs to do its thing (could change from DB to DB) is kept outside the way it needs to function, but what the application sees once the data is fetched is in the protobuff content.

ghaskins18:04:27

technically its both (we explode the protobuf back to a map on read and merge with the original record (which is essentially a map of the columns), and drop the :payload tag

💡 1
ghaskins18:04:49

but most application logic only cares about the fields that are from the PB, anyway

👍 1
ghaskins18:04:09

but as an example: the notion of a shard is generally only needed at the DB/App level, not the external representation…so the read of a row from the db would include the :shard column and any other keys exploded from the :payload record…and the app might use that :shard to do something (e.g. clean up other indices related to that record) but it wouldn’t be passing the :shard outside in, say, a GRPC response

ghaskins18:04:37

the external representation is modeled purely in protobuf

mogverse19:04:58

@ghaskins I am guessing you don't need something like Malli or Spec or another schema library with this approach.

ghaskins19:04:43

well, we do use spec in places, but in this particular part of the flow, no. protojure generates a basic clojure.spec for you, anyway

ghaskins19:04:31

where we still use clojure.spec explicitly tends to be at ingress edges, like a GRPC, to check conformity at a higher-level than what PB does

ghaskins19:04:33

for instance, protojure will generate a clojure.spec to check if field x is an int? or string? based on your .proto, but that doesnt cover cases where a higher-order conformity is needed, like a certain regex pattern for the string, etc

ghaskins19:04:08

so our GRPC functions tend to be decorated with :pre hooks that do additional clojure.spec validations

ghaskins19:04:29

(Above and beyond the basics)

👍 1
ghaskins19:04:56

but we dont need it on say DB read…protobuf is already doing the heavy lifting

ghaskins19:04:37

protojure generated those very basic clojure.spec rules based on the input proto

ghaskins19:04:46

so we never bother at that level

mogverse19:04:43

I got it! I can see you have those heavy conditionals in pre hook so that you throw a sensible exception and not rely on AssertionErrors from :pre failure 😅 (I see a potential macro here)

ghaskins19:04:23

Yeah, its not super elegant

ghaskins19:04:29

There was a thread about this recently

ghaskins19:04:10

yeah, though this is all generated code so its effectively a macro in spirit

😄 1
lukasz15:04:30

I used Avro in a similar way - to provide types for RabbitMQ consumers & producers, as well as request/response body for HTTP "RPC-style" API. Besides that I wrote a gnarly bash script to convert these Avro schemas to Typescript types and Prismatic Schemas for better integration and error messages

👍 1
mogverse03:04:02

@ghaskins in your approach, have you been in situations, where you needed an index on a new field, so you ended up running a long migration script that read the payload and updated the entry. That's the only downside I see in this approach..

Zach18:04:51

@ghaskins You mention in thread above using clojure with protobuffers. How's your experience been combining the two?

ghaskins18:04:13

@U056FU0UH0F its very good, though as I mentioned I may be a bit biased since I authored the project we use (https://github.com/protojure)

ghaskins18:04:31

protojure tries to create an idiomatic experience (clojure maps with spec generators, etc), so I think it meshes well…but I am always open to patches/suggestions to make it better

Zach18:04:00

Very cool!

noonian23:04:55

Does anyone know if the talks from this years In/Clojure will be posted online at some point? They have links to past year's talks on the website but nothing for 2024 yet

Shantanu Kumar03:05:32

Though some of these folks might track #C065VEW1M ^ @U053QAXUS @U030TQP563U @U06KGH6LB /cc @U0E90V6R0

atharva06:05:53

@U052PH695 We’re still working on it. They are being edited as we speak :) I’ll post them over here (or perhaps in #C03RZRRMP) once they are ready.

🙌 2
noonian23:05:51

Awesome! I'm looking forward to watching the talks when they are available. Thank you!

noonian21:07:02

Thank you!