Fork me on GitHub
Logan Powell12:11:06

👋 Newb to Mount here. I'm very excited to have heard about this library and I can't believe it's taken me this long to find it. I have a use case wherein I believe it to be a perfect fit. I'm working on publishing an NPM library (using #shadow-cljs ) written in cljs. I have some configs that I'd like to keep out of the library and be able to update independently so users don't have to update their library every time the config gets updated (new data sources). I've been looking through the docs and it seems that "runtime arguments" might be the right fit. Here's my first question: How would I load a remote resource (URL) and store it to be able to deref it assuming it would be loaded before any function the user uses fires off?

Logan Powell12:11:15

the remote resources will be: 1) a big .edn map ; 2) a datascript database

Logan Powell12:11:57

My solution thus far has been to pull these resources on every function call that needs them using core.async, but I'm hoping I can just "call in" these resources once per runtime instead of on every call


@loganpowell You probably don't want to use core.async here, as you want the :start expression to wait before the result is loaded.

Logan Powell14:11:42

@arnout thank you! How would I async load the resource then?


Why do you want to load the resource asynchronously?

Logan Powell14:11:45

it will be a remote resource/outside the bundle. I'll be publishing an NPM library and want to be able to update a datascript DB and a config file so that the library (functions not changing) doesn't need to be updated to take advantage of the latest config data available


Yes, I understand. Why has this to be asynchronous?

Logan Powell14:11:15

Oh, I just thought that was the only way to do a GET request (http) ?

Logan Powell14:11:23

is there another way?


Hmm right, maybe not. But in above code, the problem is that a) the config state might not be ready when you call somefunctionequiresconfig, and the (go ...) form returns a channel, not the result in =conf=.


To get this to work, you'd get:

(defn somefunctionrequiresconfig []
    (let [conf (<! @config)]
      ... magic ... ))


Which is awkward.


I'm not sure, maybe you want a loop inside the config defstate :start expression, that waits until the resource been loaded.

Logan Powell14:11:59

so, the issue is that I need @config in a number of functions that aren't defined within a go block


On a side note, I tend to advice (in the mount-lite documentation) not to use mount(-lite) in libraries, only in application code. Maybe your library API could have the function: (initialize), which returns a js/Promise (or a core.async channel, if you know for sure that your library consumers are also using ClojureScript). Then users of your library could do:

(.. (yourlib/initialize)
    (then #( ... start the rest of my app ... )))

Logan Powell14:11:03

So, I'm not wrong that the mount docs don't cover this use case...?

Logan Powell14:11:20

I really thought it seemed like a perfect fit

Logan Powell14:11:16

Currently, I've made it work by passing an arg all the way down a parent function that's housed in a go block, but this requires a lot of - hopefully - exorbitant arg passing


I guess, it's more suitable for starting things up synchronously.

Logan Powell14:11:51

plumbing the <!'en val through all the underlying children...

Logan Powell14:11:08

until it reaches the nested function that needs it


How about above approach? That way you can load your resources asynchronously, bind them globally if you want (in order not to pass it to all of your functions), and have the library consumer be in control.

Logan Powell14:11:28

Could I use an atom?

Logan Powell14:11:13

let me think about the initialize approach... how/where would I put it in?

Logan Powell14:11:41

The library is basically exported as a single function

Logan Powell14:11:54

it's not really an app

Logan Powell14:11:47

so, maybe I wrap the exported function in a promise, that's what you mean, right?

Logan Powell14:11:18

put the resource in an atom that I can use in the rest of the underlying functions?


If it's only one function, then yes, perfect. That way you let the App programmer decide how to deal with the inherent asynchronously.

Logan Powell14:11:09

yes, the function has a callback API

Logan Powell14:11:15

that seems like a good idea


Yes, you could use an atom, such that the consecutive calls are quicker.

Logan Powell14:11:35

hmm... interesting . Let me give that a go

Logan Powell14:11:53

seems simpler than using mount

Logan Powell14:11:31

also, while I have you, if loading remote resources is not a common use case for mount, are loading local configs what it's designed for?


Yes, very much so.

Logan Powell14:11:02

well then, on to the Promise land 😄


Haha, good luck!


> if loading remote resources is not a common use case for mount mount and remote resources are somewhat orthogonal, there is definitely a way to load remote resources: i.e. is loading configs from Consul before starting other states. > I tend to advice (in the mount-lite documentation) not to use mount(-lite) in libraries I fully in agreement with that: hence I believe @arnout's suggestion to just use a function is great


Always good to hear from the one and only author of mount! 😉

Logan Powell15:11:42

Thank you for following up @tolitius. I guess I was kinda also looking forward to working with Mount 😉


of course, always great to bounce ideas around


I haven't had a chance to use mount yet, but looking at the README, it looks like mount depends on the require order to choose which thing to initialize first. Does this mean that using something like slamhound (which cleans up requires and alpha orders them) could potentially mess things up?


@jvtrigueros have not tried slamhound but the only thing mount would care is the order in which different namespaces are compiled, which slamhound should not change