Fork me on GitHub
#beginners
<
2022-11-23
>
Leonid Riznyk08:11:08

Hello, I currently learning ClojureScript with Reagent and tailwind, and hot reload doesn't work, how can I fix it? here's my core.cljs

(ns frontend.core
  (:require [reagent.core :as r]
            [reagent.dom :as rdom]
            [frontend.views :as views]))

(defn ^:dev/after-load start
  []
  (rdom/render [views/app]
                      (.getElementById js/document "app")))

(defn ^:export main
  []
  (start))

1
delaguardo08:11:45

doesn't work how? are there some errors in the console?

Leonid Riznyk08:11:43

There's no errors, I run

npx shadow-cljs watch frontend
and when I save a file(views/app), changes don't affect the web page on reload

Leonid Riznyk08:11:32

and every time when I change something I need to restart the server to see the changes

delaguardo08:11:03

probably you'll find answer quicker if you ask in #shadow-cljs )

Leonid Riznyk08:11:55

thanks, I'm new to this Slack:sweat_smile:

Apple14:11:06

How do you do development with a remote machine? Do you use sshfs/samba to map remote folders to local?

Hermann14:11:32

I'm a emacs user, so I can use the excelent tramp mode (some tricks [here](https://willschenk.com/articles/2020/tramp_tricks/) ) over SSH without mounting anything.

Ed14:11:33

If you want to connect to a running JVM on another machine, you could start the REPL process and use SSH port forwarding to connect your editor from your local machine?

Apple14:11:47

Ed, yes. But changes to source code how do you commit to hd?

Apple14:11:27

Hermann, do you have to enter a long string every time you open a remote file?

Ed14:11:24

You can have a local checkout and do git operations as you would locally. So long as you can start the REPL with all it's dependencies on the remote machine, you shouldn't notice much difference, right? You can still load whole files over a REPL connection. I guess it depends why you want to use a remote dev machine. Is it too get around CPU or ram limitations locally? Or do you want to access networked resources that are firewalled off from your local box?

Hermann14:11:14

What do you mean by "a long string"? With nothing set up, you'd have to type all your credentials and stuff, but with a proper .ssh-config, it's just serveralias:~ the ~colon~ tilde opens a dired in the home folder, you can use auto-complete to navigate as if you're local. I think you could just add a remote project with projectile and jump to it using tramp...

Apple15:11:29

Ed, yeah CPU is better on the remote machine.

Apple15:11:57

Hermann, Tramp mode is good. I'm using it now. Ty!

Hermann15:11:28

Glad I could help

Rupert (All Street)16:11:04

For remote dev work I found emacs in the terminal over https://mosh.org/ runs fantastically. Mosh has 3 main benefits for me: • "Local echo" so no delay in seeing what you have typed • Fast networking using UDP packets - so no TCP delays. • Instant reconnection even if IP address changes You can switch back and forth between remote emacs and local emacs easily (all buffers are still open + repls are running). All the processing happens on the remote machine - so you can use a small and light laptop when travelling. I also use autossh to expose ports and keep them open. Both Mosh and AutoSSH are secured by SSH so they're pretty secure.

hiredman17:11:02

I do remote development by sshing into the remote machine(usual a vm) and running emacs there. Been doing that for all my work related development for 10+ years. It was how everyone was encouraged to work at my first clojure job

👍 1
hiredman17:11:06

the one downside is using any kind of gui tooling

Rupert (All Street)17:11:23

@U0NCTKEV8 - recommend giving mosh a try for lower latency if your VM is not in the same building.

hiredman17:11:56

my vm is in the same building on the other end of a fiber optic cable

👍 1
hiredman17:11:57

a lot of gui tooling these days runs as a web interface, which helps

hiredman18:11:22

e.g. I can run portal in a repl on my remote vm and view it using the browser on my laptop

Rupert (All Street)18:11:04

For GUIs that you use infrequently - I've found X over SSH can sometimes be good enough.

Apple18:11:13

hiredman, GUI emacs or text emacs? I find the GUI one much better.

hiredman19:11:04

usually in a tmux

hiredman19:11:38

I've only recently started using emacs --daemon and emacsclient, and it is pretty great

👍 1
hiredman22:11:40

the other thing is clipboard synchronization

hiredman22:11:28

generally copy stuff and pasting it into a remote text emacs is fine, but copying text from a remote text emacs and pasting it elsewhere can be really annoying

hiredman22:11:31

some overly complex code that lets me send a region of text from a remote text emacs to my laptops clipboard

Apple00:11:19

Why is copying from remote this hard? In urxvt selecting the text alone gets the copy done.

hiredman00:11:23

if you have multiple emacs windows open you can't select an entire line of the terminal because it will select across multiple windows

hiredman00:11:35

even with one window if lines wrap you'll get the \ indicator if you naively copy text from the terminal

hiredman00:11:44

I have a terminal full screen on a 1080p monitor, so I usually have at least two buffers open side by side

Apple00:11:00

oh i see. no problem here as i always break long lines. tramp-mode mentioned above also should work.

practicalli-johnny07:11:56

I've also used nrepl connection over SSH to collaborate with others on a REPL process running on the server, using the Gui version of Emacs and using VSCode Live share feature (very nice feature for real-time collaboration) I've mainly used an AWS VM and Emacs (Spacemacs) although as it's nREPL I could use Neovim, vim, Emacs, Calva, any Clojure editor. https://practical.li/spacemacs/clojure-repl/connect-to-remote-repl.html

Madara Uchiha15:11:35

Yo, what kind of tools do I have to work with recursive structures? I am trying to convert a fairly simple map structure with a recursive :children key to a nest :ul :li Reagent hiccup structure. Currently what I have is

(defn- task->ul [{:keys [id name start end children]}]
  [:ul {:key id}
   [:li name]
   [:li start]
   [:li end]
   (when children
     (map task->ul children))])
This works, but recursion scares me, I have seen walk and postwalk but I'm having trouble wrapping my head around them. I've also thought about the quasi tail-recursive solution with (loop ... (recur but I'm not sure how to approach it.

Madara Uchiha15:11:17

(def nested-gantt-sample)
{:id       "1"
 :name     "Gantt 1"
 :start    "2021-01-01T08:00:00"
 :end      "2021-01-08T17:00:00"
 :children [{:id    "2"
             :name  "Gantt 1.1"
             :start "2021-01-01T08:00:00"
             :end   "2021-01-08T17:00:00"
             :children [{:id    "3"
                         :name  "Gantt 1.1.1"
                         :start "2021-01-01T08:00:00"
                         :end   "2021-01-08T17:00:00"}]}]}

Bob B16:11:50

something like postwalk with a function that calls task->ul on maps would be pretty close given that sample, but instead of the recursive call, just leave children in a state that the walk will do whatever is desired with the entries there (because the postwalk will essentially end up running task->ul on the children by virtue of the walk)

rolt07:11:08

your solution is fine, are you really worried about stack overflow errors ? the tail recursive solution is hard to write with an immutable data structure, perhaps the easiest way is with zippers (you basically need to pass the entire structure and the location as argument to your recursive function)

robertfw20:11:18

I was looking at this and didn't see an obvious solution. I agree with rolt, both that I wouldn't worry about this in practice, presuming a gantt chart isn't going to be recursing deep enough to be a problem, and that my approach if I was worrying about this would probably be based around a zipper (but that may be because I've been digging around in zipper land recently). There are a few library options for writing transformations, they may be overkill for this but worth knowing about. • https://github.com/noprompt/meanderhttps://github.com/redplanetlabs/specter

skylize22:11:25

Is there a way to enforce that to satisfy protocol Foo, an object must also satisfy protocol Bar?

skylize22:11:31

If not, are there any good patterns for making such requirements self-documenting and/or easily discoverable?

skylize22:11:29

For example, if basic usage of a type defined by a protocol also requires implementing INext, how do I make that known through code, and not just a comment?

Rupert (All Street)23:11:09

I think it's often just done by convention + docstrings/comments. There's some reasoning behind this design decision. I can't remember exactly, perhaps because type hierarchies/inheritance are hard to extend or are too limited in some way. Or just to discourage recreating OO programming when there are better options with FP. I guess you could use clojure.spec to enforce the relationship between two types. Will be interesting to see how others respond

skylize23:11:39

I'm not overly concerned about the enforcement part. I just want something more "in the code" than docstrings or comments, that explicitly demands the minimum requirements.

Ben Lieberman23:11:24

This is a shot in the dark but is spec not an option here?

skylize23:11:21

How do you spec defprotocol?

Ben Lieberman23:11:29

I have no idea, but if it's not an existing feature it seems like it could be implemented?

skylize00:11:21

With defprotocol I am declaring "If you want the behavior of Foo , you need to implement foo." Except that's not true, because without bar , a Foo is completely broken and useless. But bar is already of the Bar protocol. I know I could use spec at the implementation layer, but at this layer, my only concern is that some implementation will be provided later.

colinkahn00:11:31

Are you looking for a compile time check? Otherwise you could in your implementation of Foo's methods do something like (foo-method [this] (assert (satisfies? Bar this)) ...)

skylize00:11:14

A compile time check would be "nice to have". My bigger concern is having the code in and around (defprotocol Foo ...) be self documenting, such that someone reading the code can scan over it and (without reading the comments or docstrings) know that "Ok, to make a Foo, I need foo and bar. So I had better go look at the code for Bar, too." I'm not sure I quite follow your suggestion.

skylize00:11:19

Oh. I see. You are suggesting that the implementation can make a runtime check. That's not really the goal. I am trying to get self-documenting code at the interface level, for the sake of someone writing an implementation at a later date.

skylize01:11:34

Thanks for the suggestion though, and to everyone else who has chimed in.

Hendrik06:11:38

Why not use schema. Something like this (I didn’t test):

(s/def :my/protocol (s/and #(satisfies? Bar %) #(satisfies? Foo %)))

delaguardo09:11:31

hm... if method foo of protocol Foo needs method bar of protocol Bar then i would combine those methods under one protocol, either Foo or Bar.

delaguardo09:11:09

there is no point having a protocol that implicitly depends on another protocol imho

Rupert (All Street)09:11:40

As discussed you can create a runtime check using (and #(satisfies? Bar %) #(satisfies? Foo %)) - with Schema or Spec or Assertion or plain functions that throws an exception. You probably can't and shouldn't try to make it a compile time exception - you can't be certain that forever the two Protocols are always required together. e.g. a Bar2 could be created that replaces Bar. Also in clojure you are not forced to implement any methods of a protocol - they are always optional.

skylize16:11:32

> there is no point having a protocol that implicitly depends on another protocol imho The implication is that Bar has meaning without Foo, but Foo does not have meaning without Bar. The most obvious and straightforward example is to depend on any protocol in Clojure core, e.g. Foo must be countable. You don't even have control of the base protocol in that case. But even if you did, that would not make it wise to go messing with it for the sake of the derivative protocol. But then how do you express in the code that Foo must be countable?

Rupert (All Street)16:11:50

> Foo must be countable The answer in Clojure seems to be either a runtime check or just don't worry about it. You have to imagine that you release Foo protocol in a library and then never update it. Today Foo must also be Countable, but you can't guarantee that it must be Countable forever. What if we have a BetterCountable protocol instead in the future? Your users would still be stuck having to use Countable too even though no one uses that anymore. Also a protocol does not force you to implement a method so even if you create a type that implements Countable - there is no guarantee that count has been implemented correctly or even at all.

skylize17:11:45

All I really want to something roughly on the lines of...

(defprotocol Foo
  {:dependencies
    {:count clojure.lang.Counted}
  (foo [this] "foo does a thing"))
Ideally this code would imply a check for count implemented via extend clojure.lang.Counted when calling satisfies, though arguments against doing that are totally reasonable. More importantly, the code itself tells the (human) reader "You need to implement count (which is currently part of the clojure.lang.Counted protocol) if you expect the Foo protocol to work as intended." Specific details about how to implement count appropriately or why it is needed, can then be relegated to comments/docstrings, or even to docs. Maybe I need to write simple a macro that just throws away the dependency declaration?

delaguardo17:11:48

> But then how do you express in the code that Foo must be countable? by using count method of Counted interface inside of another method implementation

delaguardo17:11:38

in general i try to not express that interface or protocol must depend on something. This belongs to implementation

delaguardo17:11:23

btw schema has variant of defprotocol which might help - https://plumatic.github.io/schema/schema.core.html#var-defprotocol

Rupert (All Street)17:11:24

Protocols are open for other namespaces/developers to use. The idea is not second guess how it could be used, a future developer of the code may thank you! Protocol hierarchies are an OO concept that have limitations and problems associated with them (I gave an example in a previous response). I get what you want to do and have wanted to do the same myself before, but I think your program will probably work fine without the protocol relationship baked in at code/compile time.

Rupert (All Street)17:11:24

But if you do want this then a macro could be a solution as you suggested.

Rupert (All Street)17:11:02

Also someone could come up with InfiniteFoo or RandomProbabilisticFoo that cannot be counted.

😂 1
skylize17:11:11

I am trying to make a lib where the Protocol is a major part the API. The lib will have at least one reference implementation, but the goal is for the user to be creating fully custom implementations There are 2 cooperating protocols: one describing a state monad and the other a state container. The purpose of the state-protocol is to provide dependable entry points for the state-monad, to know how it is allowed to access and update the state, while leaving the user with complete freedom on generation and traversal of data. So with Foo representing the state container, any particular Foo implementation may or may not use its own protocol methods internally. But if some method is missing, it will break the consuming transformer, and thus the entire API. It needs to be implemented. In essence, a Foo is not a Foo without all of some set of methods, because the simplified definition of a Foo is "a thing that can be correctly transformed by a Foo-managing state monad". It is possible someone could be interested in adding more ways to transform a Foo. But if a Foo cannot be transformed correctly by its corresponding monad, there is zero reason to put your data into a Foo in the first place.

skylize17:11:06

> Also someone could come up with InfiniteFoo or RandomProbabilisticFoo that cannot be counted. As per the explanation above, if InfiniteFoo does not provide the API required by FooTransformer, then it not a Foo.

skylize17:11:29

> btw schema has variant of defprotocol which might help The value-add of schema's defprotocol is type hinting the inputs. A major goal of my protocol is to have as little opinion as possible about the types of the inputs.

delaguardo17:11:00

do you have some draft pseudocode that can describe how one would use your library? I'm not sure I ever needed just a protocol from the lib so it is hard for me to understand your intention

delaguardo17:11:14

Schema can give you a hint on how to implement such macro. there are also some "gotchas" in the docstring that could help

skylize17:11:56

I could be wrong, but I don't think I would have much difficulty with the macro as described. Just seems kind of gross to write a macro who's entire purpose is to throw code in the garbage. 😄

skylize17:11:13

I'll think about how I could meet in the middle at a minimalst example halfway between Foo and the real (very much in flux) codebase.

delaguardo17:11:48

try it :) some times Clojure can give very hard time when one want something "unusual"

delaguardo17:11:39

btw. your Foo-managing state monad suppose to be provided from your library? if so then it could have checks if an object implement specific protocol or even protocol method

skylize22:11:06

Good point. That could cover the enforcement side, if I decide it is worth it. Does not, in my opinion, cover the self-documenting concern. If I am assuming that reading the comments is too much to expect from the user, then digging through the other protocol for runtime checks is clearly even worse.

Rupert (All Street)00:11:59

Thanks for your detailed description. Your description does not: • explain why it is exempt from the BetterCountable scenario that I mentioned. • Or exempt from someone wanting to create a type that only takes one of your protocols e.g. for unit testing. • Or from the fact that a protocol does not guarantee that the associated methods are actually implemented or correct (which seems to make it pointless to force the protocol). Even your specific usecase overcomes the above, can you see that there are lots of scenarios where protocol hierarchies are a bad idea and should be avoided? You can easily create a runtime function that is "opt in."

(defn is-foobar?[s](and (instance? Foo x) (instance? Bar x)) 
This allows users to opt in to your check without being forced at compile time.

skylize00:11:00

Yeah. Your argument against compile-time checking makes sense, as Clojure currently stands. In my description, BetterCountable should be fine so far as it means that a Foo implementation will reliably include a`count` method. My primary concern, again, is that the code of (defprotocol Foo ...) is somehow providing self-documentation that it needs certain methods, our example being`count` (with the general expectation that it would come from implementing certain existing protocol(s), in this case Counted). As for the generic argument against hierarchies, I see no reason that BetterCountable could not declare provides Counted. But, of course that is not a currently existing feature of the language.

👍 1
Rupert (All Street)00:11:40

Excellent - the language is extensible through macros if you do want to add the compile time checks. But I just felt it worth highlighting that this is likely a case of "YAGNI" - lots of Clojure code works fine without it - and probably your use case too.

Ed12:11:35

You could always use composition instead of inheritance and have foo take a bar as one of its arguments. If you really want to have them be the same object, then you could always provide a function that has one less arity and passes the same object to foo twice? Or am I misunderstanding the problem?

skylize13:11:22

I am extending nil with the protocol to get nil punning behavior. So no, I cannot get that from composition.

delaguardo14:11:55

in Clojure you can't extend primitive, can you?

skylize14:11:36

Extending nil seems to work. 🤷

skylize14:11:42

I just define all the protocol methods to return nil.

delaguardo14:11:17

i mean extend function

skylize14:11:31

I have not specifically tried extend function. But it works with fine with extend-protocol and with extend-type, so I assume that translates to also working with extend.

delaguardo14:11:35

i can be easily wrong) do you mind share how you extend nil with protocol?

skylize14:11:26

(defprotocol IFoo
  (foo [this]))

(defrecord Foo [x]
  IFoo
  (foo [_] x))

(foo (->Foo "foo"))  ; foo
(foo nil)            ; Execution error

(extend-type nil
  IFoo
  (foo [_] nil))

(foo nil)            ; nil

skylize14:11:51

And you get the same with extend-protocol.

(extend-protocol IFoo
  nil
  (foo [_] nil))

skylize14:11:49

And I don't know why you would want to, but you can do whatever you want here, just like any other extension.

(extend-protocol IFoo
  nil
  (foo [_] :bar))

(foo nil)            ; :bar