Fork me on GitHub
#beginners
<
2020-12-16
>
andarp07:12:59

As someone just learning Clojure, all the resources from the recent years make out Leiningen to be the only sensible choice, but lots of recent discussions are about deps.edn. Is there a change happening in the community? I get that lein does a lot more than handling deps and building, but as a beginner I care more about having a simple way to get going and not having to understand a big tool. Would it be foolish to focus on deps.edn initially? I currently have no interest in packaging up or deploying Clojure code.

didibus18:12:28

If you don't care about packaging or deploying code, I'd actually venture deps.edn might actually be better

didibus18:12:25

I also need to correct you though, all build tools are fully viable, Lein, Maven, Clojurephant, Boot, Tools.Deps, etc.

didibus18:12:36

There's someone somewhere using any of them in prod successfully

didibus18:12:55

But Lein will have the best tool support (like chance of it playing nice with your IDE and other tools), followed by Tools.Deps (which is slowly catching up to Lein in that realm), followed by Boot.

noisesmith07:12:01

for your use, either should be about equal, go with what your book / tutorial uses and if not using one of those deps.edn is simpler (it does less)

noisesmith07:12:47

@anders152 they are dep management tools, many projects have configs to use both

andarp07:12:02

:thumbsup: thanks!

noisesmith07:12:05

it's easy to switch if it comes to it

practicalli-johnny09:12:20

@anders152 using Leiningen or Clojure CLI tools (deps.edn) shouldn't make a difference to the Clojure code you write. There are a few Leiningen plugins that provide code for you, e.g. lein-ring, but other than that you can largely swap between the two ways of running and building Clojure projects. Simply adding a deps.edn file that contains {} is enough for projects that only have Clojure as a dependency. Adding {:deps {,, ,,}} when using additional libraries. If you are interested in using Clojure CLI tools, take a look at https://practicalli.github.io/clojure/

🙌 6
Silas Mordrek10:12:58

Hi everyone!. I'm wondering if there is a "from the ground up" course or guide to start creating mobile apps for Web+Android+iOS using clojure or clojurescript (with one single codebase). I'm willing to pay. I'm currently about to start a big social network project for a client and I'm very tempted by what clojurescript has to offer since I love CommonLisp and like clojure. I've seen a lot of names floating around like "react-native" "reagent" "expo", etc.. but can't seem to find a guide that helps me make an app from the ground up. I've tied a couple of tutorials but all seem way to verbose and some outright don't work, they break. Is there an entry point you can recommend?. I don't need a full-stack clojure solution, which many tutorials I've seen asume. I want to create client side apps that can connect to my existing server using websockets (or REST if there is no websocket support available). Even if it's not really a "one code base solution" but as long as it's more or less the same technology stack, I'd be happy. Something like: • "Clojure/Script + A for Web" • "Clojure/Script + B for Android" • "Clojure/Script + C for iOS" If there is not ONE guide or tutorial but someone can point me to a list of resources or can share their learning material I would appreciate it a lot. If something close to a "one codebase solution" for my client side apps is feasible and someone can help me with it I'd be willing to pay for the help or pay for the course, etc...

V11:12:18

I have not taken the course myself. But it seems to be the closest thing to cover some of the things you mention

V11:12:48

He also has some courses on clojure for web

Silas Mordrek12:12:13

This seems very helpful, thanks. It only covers iOS though, so if anyone can provide more material for Android and Web I'd appreciate it. I'm currently going through a React-Native tutorial and it seems to work just fine. https://reactnative.dev/docs/environment-setup But when I try this clojurescript+re-natal tutorial, it breaks, I'm still trying to find out why. https://medium.com/mindorks/building-mobile-apps-ios-and-android-with-clojurescript-4600235f826c

Sam Stowers17:12:40

@U01H16Y2F8T I'm also a beginner, but my impression (as a React Native developer) is that there isn't great support for CLJS + React Native (or other compiling-to-native tools) right now. Browser apps are well supported with Reagent etc, but the mobile side lacks resources and active support. Given the pace of change with React Native alone (especially if you use Expo), TypeScript may be a better choice. Or Flutter

yunior23:12:54

Hi. I was able to start a mobile app with clojurescript using tools.deps and https://github.com/bhauman/react-native-figwheel-bridge

yunior23:12:05

With that I can have the same base code for andoid and iOS. With some modifications to deps.edn I could cover Web as well.

David Reno13:12:34

I’m connected to a remote repo for development (raspberry pi project). I’ve been using Emacs tramp to edit the files remotely (and cider-connect) but would prefer to edit the files locally. It seems to me that the only way to do that is to have the source code repo at both locations. Seemingly, when I need to add a new library, I would remotely edit the project.clj and restart the REPL, all other files I would edit locally. Then I would need to git commit both repos. Am I understanding this correctly?

Renato Alencar13:12:44

I guess you could probably use rsync for that

Renato Alencar13:12:11

Also, if you're connected to a nrepl session, you could just evaluate the buffer in order to "update" your code. But, if you need to reload a modified file you would need to update that on the server.

David Reno13:12:27

yeah, using nrepl. I have no issues with evaluating or loading the buffer for the code I’m developing (which is effectively local since it comes from my local emacs buffer). It’s just the idea of needing to restart the REPL in order for the dependencies to download. It seems confusing to be committing from two different machines.

Renato Alencar13:12:32

I think this could help you

Renato Alencar13:12:12

It isn't the exactly thing you want to do, but I guess it could at least improve your productivity.

👍 3
David Reno13:12:27

Thanks for the suggestion. This is going down the road of switching from lein to clojure tools: https://insideclojure.org/2018/05/04/add-lib/. I saw @U04V70XH6 demo this last night and I was trying to take baby steps in the direction of simplifying my toolchain (by removing tramp) without committing entirely to tools.deps.alpha, but that might be easiest at this point.

Renato Alencar14:12:33

Great, the simpler the better.

practicalli-johnny17:12:07

Is editing files on the Raspbury Pi via Emacs Tramp slow? I've started using Emacs tramp with a remote server and havent had any issues. I ssh into the remote server via a terminal, create a project if required and start the REPL process. I start the repl with nrepl and cider libraries. Then I open the project files on the remote server via Emacs tramp (which was so easy). Then with the project files open in Emacs, I run cider-connect-clj If I need to restart the REPL project, then I just switch back to the terminal and do so there and use cider-connect when its back up. Details are here (sorry its a bit rough) https://practicalli.github.io/data-science/collaborative-coding/shared-server.html Would be interested to know whats different for Raspbury Pi. Thanks.

David Reno17:12:34

Doing similar with rpi and it’s fine. The issues are around complexity. For example, projectile works differently with tramp connections. Also, I have continually had performance problems with emacs and am looking to reduce the features in use to try to make it more reliable.

David Reno17:12:31

WRT performance, emacs occasionally hangs or pauses/blocks with high CPU. Normally tramp is very performant.

practicalli-johnny18:12:15

Thanks for sharing, much appreciated. I've only recently started, so have found the constraints yet, so goo to know.

ichise.masashi14:12:59

I got an error. What am I doing wrong?

user=> (def my-array (make-array Integer/TYPE 2 2))
#'user/my-array
user=> (aset my-array 0 0 100)
Execution error (IllegalArgumentException) at java.lang.reflect.Array/set (Array.java:-2).
argument type mismatch

manutter5114:12:33

Clojure "integers" are actually longs, maybe that's it?

andy.fingerhut14:12:08

(aset my-array 0 0 (int 100)) does not give an error

andy.fingerhut14:12:33

Also no error if you change it to an array of Long/TYPE

ichise.masashi14:12:11

Thank you, I got it ☺️

ichise.masashi14:12:11

user=> (type 100) java.lang.Long

Chicão14:12:49

Hi, I want to create some kind of multiple auth to version my api with public routes and user routes and admin routes. May someone have some link explaining how auth-rule or any like that works on compojure ?

andarp18:12:16

Hm, how do I check if a vector contains a specific value? I assumed contains? but now learned that this checks for the presence of keys, and in the case of a vector thats the index...

Renato Alencar18:12:57

Values in a vector are not "indexed", so you can't actually check presence with O(1) complexity. But you iterate over the vector.

Renato Alencar18:12:16

A simple way to do that would be using some, like this.

andarp18:12:34

I just realised that this can be solved with a set, which makes a lot more sense...

Renato Alencar18:12:38

(some (partial = value) my-vector)

🙌 3
andarp18:12:21

Thanks, that points me to the right way of thinking @U014XLW158T!

andarp18:12:49

Speaking of which, if I have code that looks like this: #(contains? #{"a" "b"} %) Will the set be created every time the anonymous function is called?

andarp18:12:45

I know how this works in most languages (the answer being yes), but I just want to double check how it works in Clojure..

Renato Alencar18:12:36

Probably not, since this is a complete literal value without any references in it.

Renato Alencar18:12:51

But, let me check.

🙌 3
Renato Alencar18:12:43

I had to do a little bit of work but here it is. This is your anonymous function compiled.

import clojure.lang.IFn;
import clojure.lang.PersistentHashSet;
import clojure.lang.RT;
import clojure.lang.AFn;
import clojure.lang.Var;
import clojure.lang.AFunction;

// 
// Decompiled by Procyon v0.5.36
// 

public final class a$fn__38 extends AFunction
{
    public static final Var const__0;
    public static final AFn const__1;
    
    static {
        const__0 = RT.var("clojure.core", "contains?");
        const__1 = (AFn)PersistentHashSet.create(new Object[] { "a", "b" });
    }
    
    public Object invoke(Object p1__37_SHARP_) {
        final IFn fn = (IFn)a$fn__38.const__0.getRawRoot();
        final AFn const__1 = a$fn__38.const__1;
        final Object o = p1__37_SHARP_;
        p1__37_SHARP_ = null;
        return fn.invoke((Object)const__1, o);
    }
}

🙌 3
Renato Alencar18:12:32

It turns out the set is created when the generated class for the function is loaded.

Renato Alencar18:12:54

That's because your set only has literal values, which can be used as a constant instead of recreating every time you invoke the function.

andarp18:12:13

Nice! That's what I was hoping.

andarp18:12:24

Thanks a lot for the digging.

didibus18:12:46

No, the set is created at read time

didibus18:12:03

So it won't be created every time the function is called

didibus18:12:24

That goes for every reader literal, those are always created at read time, thus only once when the namespace is loaded

Renato Alencar18:12:01

Oh, that's what I was missing. I was just considering projects using AOT compilation, which is not always the case. And sure, every literal value is evaluated and instantiated in read time.

didibus18:12:52

Ya, but you're correct in that, if there is a variable in it, then it will be differed to runtime, since it can't create the set until there are actual values for all variables in it

didibus18:12:32

The difference I'm pointing out is that, if instead of a reader literal, you used a function, like:

(fn [] (hash-set 1 2 3))
This will always create the set at runtime, every time the function is called a new set will be created with 1 2 3 in it.

didibus18:12:30

But reader literals will be at read time instead, if everything is resolvable at that time

didibus18:12:20

Hope I'm making sense

Renato Alencar18:12:36

It totally does

clyfe19:12:03

tip: sets are functions: called with 1 arg they return it if in set, nil if not

👀 3
clyfe19:12:31

(#{1 2} 2) ;; => 2

andarp20:12:30

Wow, great tip. I love how so many things can be used as functions in Clojure.

andy.fingerhut21:12:10

Minor word of warning: (#{false} false) returns false and (#{nil} nil) returns nil, which are logical false values in conditional expressions, e.g. if, cond, if-let, etc. If you avoid those values when using that technique, you should be fine.

andarp20:12:22

Is there a seq function for creating a sequence of pairwise values from an input seq? E.g... (1 2 3 4) should create ([1 2] [2 3] [3 4])

bronsa20:12:44

user=> (partition 2 1 [1 2 3 4])
((1 2) (2 3) (3 4))

andarp20:12:17

Oh, nice! Thanks a lot.

ribelo20:12:11

guys, I have a bizarre problem, probably with maven

ribelo20:12:44

ribelo@ribelo-xps ~ [1]> clj
Error: Could not find or load main class clojure.main
Caused by: java.lang.ClassNotFoundException: clojure.main

seancorfield20:12:42

@huxley What does clj -Sforce do? Also clj -h and clj -Sdescribe just so we have a bit more detail.

seancorfield20:12:20

(has clj been working for you before? If so, what did you change on your system between it working and it breaking?)

ribelo20:12:59

-Sforce helped.

seancorfield20:12:24

Stale cached dependencies. Maybe you deleted something or updated something that broke the previously cached dependency information.

ribelo20:12:36

From time to time I have a problem while working on a project. Dependencies seem to disappear.

ribelo20:12:27

Syntax error (FileNotFoundException) compiling at (clj_cli/core.clj:1:1).
Could not locate clojure/core/async__init.class, clojure/core/async.clj or clojure/core/async.cljc on classpath.

ribelo20:12:23

Removing $HOME/.m2 always solved the problem.

ribelo20:12:32

However, today it has completely broken down clojure.

seancorfield21:12:14

Ah, yes, if you remove ~/.m2 you are going to break all of your cached dependencies because they're going to point to files you have deleted.

seancorfield21:12:02

Much better to leave ~/.m2 alone and just use -Sforce with clj / clojure if your dependencies seem to be broken.

andy.fingerhut21:12:02

You can also delete ~/.m2 and then remember to use -Sforce , not in one but in all projects on that same system, but yeah, that can be easy to forget. Deleting ~/.m2 is a pretty big hammer that shouldn't be needed very often.

clyfe21:12:49

Some library has a protocol and a record implementing it; I want for myself a slightly bit different record implementing the same protocol. Lots of copy paste follows; because "Concrete derivation is bad" I get. Is there a construct to refine the first record and avoid all the copy paste?

phronmophobic21:12:10

there are ways to automatically construct new records that are slight modifications of old records. how would you like your record to be different compared to the first?

phronmophobic21:12:37

what kind of refining do you need?

clyfe22:12:07

same implementation with 1 method different

clyfe22:12:26

(refine SomeRecord AProtocol (changed-method [args] ...) ...)

clyfe22:12:33

=> new record, no inheritance, some methods have different impl

phronmophobic22:12:27

as long as you can convert SomeRecord to OldRecord, you could have refine be a macro that expands to an extend-type call that converts SomeRecord to OldRecord and calls the method on it for any method without a provided implementation required for AProtocol

phronmophobic22:12:07

I thought there was a cleaner way to extract a method body from a defrecord, but I can't seem to find it

didibus22:12:29

Hum, seems you're doing OOP

didibus22:12:44

And there's probably a Functional way to structure whatever you are doing instead

didibus22:12:10

But, to answer your question, what you do is have your protocol methods actually be functions. And when you extend a record to it, you just choose the set of functions you want for each protocol method

phronmophobic22:12:33

I think the problem is that the functions that they want to reuse are from another library that already didn't do that

didibus22:12:34

Oh, I see.

phronmophobic22:12:54

yea, otherwise. 💯 agree

didibus22:12:30

Then I would probably use composition instead of inheritance in this case

didibus22:12:07

But, I never thought how to do that with records hum... :thinking_face:

didibus22:12:17

Might not be possible, or at least I can't think of a way

clyfe21:12:55

Also: multimethods have defaults and hierarchies, protocols don't; why?

phronmophobic21:12:38

from https://clojure.org/reference/protocols: > There are several motivations for protocols: > - Provide a high-performance, dynamic polymorphism construct as an alternative to interfaces > ... > - Support the 90% case of multimethods (single dispatch on type) while providing higher-level abstraction/organization I think the difference between multimethods and protocols is that protocols can leverage jvm bytecode instructions to improve performance where some of the flexibility provided by multimethods cannot leverage the same optimized bytecode instructions

noisesmith23:12:03

if clojure implemented concrete inheritance for records this wouldn't be an issue, but clojure data objects don't support that, multimethods can implement a similar delegation (with :default etc.) but don't use vm object inheritance (and are not data objects)

noisesmith23:12:07

clojure as implemented is pretty opinionated about extension via composition rather than inheritance

clyfe18:12:33

(extend-type default P (f [] ...)) is painfully undocumented

didibus19:12:02

Wow, is that a real thing?

didibus19:12:14

It seems to maybe be ClojureScript only? I get Unable to resolve symbol: default in this context in Clojure

clyfe19:12:53

Yes, cljs only, and is documented in extend-type's docstring (just cljs).

clyfe19:12:15

I was looking for it in clj docstrings...

clyfe19:12:05

seems cljs uses default/:default in a number of places, see channel chat linked from my comment there

clyfe18:12:33

(extend-type default P (f [] ...)) is painfully undocumented