Fork me on GitHub
#clojure-nl
<
2019-12-12
>
gklijs07:12:09

Morning, you missed two nice talks yesterday 😉

🙌 4
Mno08:12:58

Meowning 🐈

borkdude10:12:25

@yannvahalewyn Forgot to mention it yesterday, but yada deserves a mention. It's both data driven (data > functions > macros) and fully support the async story.

yannvahalewyn10:12:04

Definitely! I was a bit short yesterday with making the slides and didn’t include some important ones.. Other ones would be: • Git! Because it has a plumbing and porcelain (composition vs arrangement) • How Datomic is build: it just has a simple protocol and can compose multiple persistence backends like S3, Postgres, H2, DynamoDB..

borkdude10:12:36

On that note: crux

borkdude10:12:56

but maybe that emphasises composition and not so much arrangement yet

yannvahalewyn10:12:05

not emphasizing arrangement in a library is perfectly valid imo, let the users arrange it right. Tailwind does this, but also datomic in the sense that you as a consumer can arrange your own datomic out of one or more of those backends

yannvahalewyn10:12:59

I think a good library offers at least the composition, and may offer one way to arrange them as a “get started quickly” idea but allows to be “remix”’ed 🙂

yannvahalewyn10:12:04

developers are smart people and I think we are afraid sometimes to open up some internals to the public

yannvahalewyn10:12:48

maybe because of the ideas of encapsulation coming from years of OO, but I don’t really know why exactly

borkdude10:12:54

@stex I also enjoyed your talk btw. The thing about reactions in subscriptions: last time I asked about this in the #re-frame channel (because the docs hinted at this being not recommended anymore) the answer was: events should always be triggered / dispatched as a result of user actions, not by subscriptions

borkdude10:12:09

e.g.: user opens table -> event -> http request

borkdude10:12:44

but we've used all kinds of different approaches and if they work, they work 🤷

yannvahalewyn10:12:31

I think the idea is that when a new subscription is consumed, it probably means a new view just got rendered or some input data changed which usually is because of a user action, so @stex’s ideas still align with that no?

yannvahalewyn10:12:42

I still like it more if say a table gets shown after a click, to let the table declare what he needs instead of guessing what the table will need when the click happens

yannvahalewyn10:12:10

think in terms of how fulcro co-locates views and queries

Mno10:12:39

Related to your talk I kept wondering if Convention and Composability are mutually exclusive

Mno10:12:39

You could have a convention be a composable standard.. or even the other way around.. somehow make composable conventions

yannvahalewyn10:12:50

yeah the order is still a bit unclear indeed

Mno10:12:47

I'd stick to having a convention on composability for a certain standard since, composable standards would increase the overhead by requiring participants to remember which conventions were composed.

yannvahalewyn10:12:52

well, if I think about it a bit I would say composition over convention stands. You want the base to be totally composable with less “magic”, and then on a higher level, the arrangement level, that’s where you can use conventions more

Mno10:12:09

there seems to be a conflation between convention and "magic" I don't necessarily agree has to exist.

Mno10:12:33

But I have to work today.. so I have to focus for a while 😅

yannvahalewyn10:12:55

sure, I don’t mean to say they always come together indeed

yannvahalewyn10:12:19

But when you execute conventions tightly sometimes this can introduce magical elements

yannvahalewyn10:12:54

which is fine, if it’s on the high level arrangement

Mno10:12:55

Seems reasonable

Mno10:12:12

I look forward to you proving your ideas on RAD

yannvahalewyn10:12:59

It will be Tony that proves his ideas haha, but thanks!

yannvahalewyn10:12:48

thanks for coming yesterday and listening to what we have to say! There were some really interesting conversations that came out of it and I sure learned a lot! partywombat

gklijs10:12:56

From the javascript world they speak highly about using the Apollo React client with a GraphQL backend, to handle state in a nice way. Should also be able to build something similar, not unlike your solution, in reframe. So you could just say witch data you need in the view, and on the background it does the http request if needed (using re-graph). Not sure how fulcro works, but I guess it's something similar?

Mno10:12:39

I learned that if you tell people to tell the person next to them 1 fact about them it will start massive amounts of conversations

yannvahalewyn10:12:39

Works in bars too

🍹 4
stex11:12:40

@borkdude Thanks! I’ve read the addition in the docs does surprises me that they don’t encourage this approach. It’s basically the approach I’ve shown in the first part of my talk 😄. If you trigger http-requests only by user-interaction, you still need to be aware of all the dependencies: user opens table -> event -> what http requests are required for the table? It’s also hard to reuse components this way. Let’s say your page have 50 pages and on 20 of those you include a small widget which requires remote state. Do you really want to couple the user entering a certain page with dispatching things for the specific widget? What if a page stops using that widget? What if you add an extra page? What if you have a large combination of widgets and pages? I do understand this introduces more side-effects to subscriptions, which is less pure. But I’m fine with that 😄.

kwrooijen11:12:36

@borkdude Would it be a good idea to have the ability to set class paths in an environment variable? (instead of --classpath argument. e.g. export BABASHKA_CLASSPATH=... ) ?

borkdude11:12:20

@stex We have an event for each page open, which will trigger the init events for the widgets, which will then trigger fetching their data

kwrooijen11:12:34

Which would eval before BABASKA_PRELOADS

borkdude11:12:09

@kevin.van.rooijen I think that makes sense, planck also has PLANCK_CLASSPATH

borkdude11:12:42

@kevin.van.rooijen Note that nothing from the classpath gets loaded unless you use require

kwrooijen11:12:14

True, but then you could require in your PRELOADS, correct? I think that would actually be really helpful

borkdude11:12:52

yeah, you can do that

borkdude11:12:11

I'll make sure that works

borkdude16:12:59

@kevin.van.rooijen Are you interested in testing a version with BABASHKA_CLASSPATH?

borkdude16:12:37

if so which OS?

kwrooijen16:12:00

Sure, but I’m not sure if I have time tonight, probably tomorrow. MacOS Catalina

kwrooijen16:12:40

All right, I’ll let you know if I get the chance tonight

kwrooijen18:12:55

I tried out your binary, seems to work fine!

borkdude19:12:38

Thanks! I wrote a unit test for it meanwhile and released 0.0.41

borkdude19:12:56

More info in #babashka

kwrooijen19:12:07

Great, I’ll update to it now 👍 Thanks

stex11:12:10

@borkdude That works for simple pages, yet you are having the same problems right? You need to know which widgets are used on the specific page.

borkdude11:12:37

@stex we know that already, since we put them there ourselves 🙂

borkdude11:12:22

anyway, not to criticise any approach, just thought I'd mention the comment from the re-frame folks

borkdude11:12:35

ask in #re-frame if you want more info I guess

stex11:12:44

@borkdude Haha, but you need to keep the widgets on the page in sync with the dispatch 😄 .

borkdude11:12:48

they discouraged triggering events from component didMount etc

stex11:12:15

Yep, I’ll discuss in #re-frame if I have some more time. Just wanted to check if you’re having the same issues. Sounds like you’re fine with the simple approach, do think this will get hard when you do traditional REST on the backend and need to join in the frontend.

borkdude11:12:46

@stex We have quite a complex page with lots of widgets, sometimes depending on each other. And we have a REST backend.

borkdude11:12:15

We do have a client-side API abstraction with a cache (key) like you described

borkdude11:12:56

Like I said, if it works, it works. I should maybe look into fulcro one day, but that couples the backend and the frontend, which was not an option for us

gklijs11:12:08

That's the main reason I didn't really look into fulcro yet. I like to keep options open to another back and/or frontend. Like I can test different GraphQL server implementations and to end using the same re-frame code. Because it's composable.

yannvahalewyn12:12:41

@gklijs So to answer your question about Apollo: I’m not familiar with apollo, but based on how you describe it: yes. this sounds quite like Fulcro. But fulcro also does something extra that maybe Apollo doesn’t. It composes a query with an ident. These two together are a very powerful combination. It’s the how and the what. If you only have a query, all you can do is fetch the data. If you have a query and an ident, you can do loads more things: you can merge queries, nest queries, normalize and denormalize a client db, make mutations, .. example: Say you have a widget that shows a user’s name and another widget with the same user’s email. They both will have the same ident. So if both views render at the same time, the queries will be merged and executed together. The result will go in a normalized client db:

{id {:user/id id :user/name name :user/email email}}
If both widgets render at different times, two fetches will occur but the results will be merged, resulting in the same shape. You can’t do this if queries don’t have an ident. Other examples would be forms. You query the data you need for the form, but you can mutate that same data in the same spot in the app-db. If you have nested forms you can normalize both querie-results in the app-db, making mutations trivial, but have a denormalized tree to make the rendering of the form easy. I think Fulcro doesn’t deserve the adoption it deserves. It’s extremely powerful, but based on a very simple idea. I don’t think it’s easy to understand though, and people miss these kinds of brilliant aspects of Fulcro and put it away.

yannvahalewyn12:12:27

@borkdude This is an interesting conversation. It seems that Stefan, me and the fulcro people think colocated queries and views are a great idea, and you + re-frame people don’t. Maybe we could talk about this a bit more

borkdude12:12:40

@yannvahalewyn Om(.next) has this too. I don't think it's a bad idea nor do I have a strong opinion on it. I just pointed out that the docs linked to in the presentation yesterday are effectively deprecated and the re-frame folks suggest a different approach.

borkdude13:12:37

(different approach than dispatching events in subscriptions I should say)

gklijs13:12:02

I don't know what Apollo does either, but sounds similar. You can also add things to the GraphQL schema for client site only date, like 'seen'. I think it also does aggregation maybe even across components. It can replace redux entirely. One of it's problems is because the cashing, and because the db is based on the GraphQL schema there could be inconsistencies. For example if you have a UserPublicView and an UserFriendView, having some fields in common they could diverge.

yannvahalewyn13:12:47

That last part is solved by fulcro's idents. As for caching, yes. GraphQL and pathom can't really use http caching. You have to bring your own solution to the table

yannvahalewyn13:12:05

@borkdude fulcro was originally forked from om.next I believe. Interesting to realize we're actually using reframe with om/fulcro ideas

borkdude13:12:50

maybe you can blog about it how it exactly works (or an open source lib), that's maybe a better starting point for a discussion

borkdude13:12:09

or publish the talk

yannvahalewyn13:12:16

Both are on the table, it will depend on @stex :)

borkdude13:12:22

it's pretty interesting indeed if you combine the good parts of re-frame and fulcro, without having to change your backend. arranging ftw!

yannvahalewyn13:12:24

Or "how to make fulcro ideas work with rest" could be a nice blog post idea

thomas13:12:21

Re Fulcro was first based on Om.Next, but now it isn't anymore.

thomas13:12:43

We use it here at Avisi, and I am still trying to get my head around it.

borkdude13:12:42

I'm hoping we can get a nice fulcro rad clojure rails killer

👌 4
borkdude13:12:58

with all due respect to rails of course

thomas13:12:10

Tony Kay is working on something like that.

Mno13:12:22

Hey if it's better then it should kill it!

Mno13:12:09

(keep in mind this is an absolute better (as in better at everything all the time) and so is impossible, Healthy competition and options are always good.)

gklijs13:12:48

I'm considering building something on top of re-graph that does some of the things, but I'm considering a lot, and might not be a good idea to try to build something like that when I won't be using it in production.

yannvahalewyn13:12:55

@thomas I’m actually getting involved with that project, I think it will be great

thomas13:12:21

aah yes you mentioned that... it would be great indeed.

borkdude13:12:26

@yannvahalewyn be sure not to include a dependency on nokogiri

thomas13:12:33

looking forward to it very much

yannvahalewyn13:12:27

@borkdude Crap, running git revert head right now

borkdude13:12:53

as for db options, will it consider triplestore-like dbs like datomic / crux / datahike as well as rdbmses?

yannvahalewyn13:12:16

not out of the box, but attributes will just be open maps so you could implement your own backends, or the community could make plugins for them

yannvahalewyn13:12:17

right now the plan is to make it work for sql and datomic, two very different backends. We avoid the ActiveRecord style where all follow the same protocol, and the idea is to offer different solutions for both, but not make them transparently swappable. I think that’s a dangerous road to take.

borkdude13:12:58

so the answer is yes

borkdude13:12:33

when I open the README I see npm install gem install foreman Makefile.. kinda scary

yannvahalewyn13:12:59

I added foreman because the dev setup is a bit convoluted, you can ignore it if you start the transactor and repl by yourself

borkdude13:12:15

@yannvahalewyn imo requiring two "foreign" package managers for a Clojure RAD framework is not attractive

yannvahalewyn13:12:22

haha yeah sorry

yannvahalewyn13:12:28

Should’ve gitignored them, thought maybe other devs already have foreman and this way they could start it all up with one command. Do you have other tools / techniques that make this easy?

borkdude13:12:09

@yannvahalewyn > Setup For any contributors wanting to run this Run what? why?

borkdude13:12:03

is this only for running Datomic? Why do I need Datomic?

borkdude13:12:13

docs aren't really clear, sorry 🙂

borkdude13:12:14

is it for running multiple build steps at the same time? if you're already using npm for shadow-cljs you could also use it for that? or just bash? https://github.com/fulcrologic/fulcro-rad/blob/develop/Procfile.dev

borkdude13:12:15

I would say: just type in make foo

borkdude13:12:30

make is probably fine

skuro13:12:30

question for the clojurescripters, I'm having some troubles with advanced compilation which go beyond my understanding. I've a function (ns my.namespace) (defn ^:export init [] ...) and I give the browser some HTML with a <script>my.namespace.init()</script>

skuro13:12:45

with :optimizations :none it works

skuro13:12:17

with :optimizations :advanced it fails with my is not defined

skuro13:12:50

any clue where to look? I'm a bit lost at the moment

borkdude13:12:35

@skuro what have you used as the argument to -c in the compiler options?

skuro13:12:30

mhhh I do not invoke the compiler manually

borkdude13:12:52

@skuro if you start typing my in the console do you see anything appearing? I think in advanced the namespaces might be collapsed into single names

skuro13:12:55

:cljsbuild  {:source-paths ["src/cljs" "src/cljc" "env/prod/cljs"]
             :compiler
             {:output-to     "target/cljsbuild/public/js/app.js"
              :output-dir    "target/cljsbuild/public/js"
              :source-map    "target/cljsbuild/public/js/app.js.map"
              :optimizations :advanced
              :infer-externs true
              :pretty-print  false}}

skuro13:12:28

that's the build config

borkdude13:12:42

(I mean the browser console)

skuro14:12:04

nothing shows up

skuro14:12:09

nothing at all

skuro14:12:18

just hits from my previous console history

borkdude14:12:50

maybe a question for #clojurescript

yannvahalewyn14:12:15

@borkdude Make is not fine when there are multiple async processes to be started. Anyway, maybe it’s way too early to finetune contributor workflows

borkdude14:12:33

@yannvahalewyn I mean Make as a "foreign" build tool, it's generally accepted and installed

borkdude14:12:49

although I don't know if you'll exclude Windows users 😛

borkdude14:12:53

docker compose could also work maybe.

gklijs14:12:02

@skuro think you need to add :main my.namespace to the cljs build options

borkdude14:12:51

-m, --main ns Call the -main function from a namespace with args

skuro14:12:04

:main
Specifies an entry point namespace. When combined with optimization level :none, :main will cause the compiler to emit a single JavaScript file that will import goog/base.js, the JavaScript file for the namespace, and emit the required goog.require statement. This permits leaving HTML markup identical between dev and production.

skuro14:12:27

not sure if it has an effect on advanced optimizations

borkdude14:12:27

-c, --compile [ns] Run a compile. If optional namespace specified, use as the main entry point. If --repl follows, will launch a REPL after the compile completes. If --server follows, will start a web server that serves the current directory after the compile completes.

borkdude14:12:30

I think you need that one

borkdude14:12:50

but that's a cljs.main argument

skuro14:12:56

yeah, indeed

borkdude14:12:40

but again, not using a plugin

skuro14:12:09

alright, thanks for now

skuro14:12:18

I'll dig deeper and resort to #clojurescript if need be

skuro17:12:50

alright, just for the curious: I found where my cljs issue was, there was an actual exception in some init code that crashed the main script before the inline call to init() ran

🎉 4
🐛 4
skuro17:12:03

fixing that exception made it work once again

skuro17:12:20

so just ^:exporting the names suffices

borkdude17:12:20

nice, congrats