nbb

jasalt 2025-03-17T07:25:03.857099Z

Testing https://github.com/f/omelette shell command line argument auto completion library (bash, zsh, fish). The https://github.com/f/omelette/tree/master?tab=readme-ov-file#autocompletion-tree looks interesting, maybe it would be convenient with lisp data structures. Basic demo https://codeberg.org/jasalt/nbb-omelette:

🔥 1
jasalt 2025-03-17T07:27:35.389029Z

It generates this clump for bash/zsh:

### hello completion - begin. generated by omelette.js ###
if type compdef &>/dev/null; then
  _hello_completion() {
    compadd -- `hello --compzsh --compgen "${CURRENT}" "${words[CURRENT-1]}" "${BUFFER}"`
  }
  compdef _hello_completion hello
elif type complete &>/dev/null; then
  _hello_completion() {
    local cur prev nb_colon
    _get_comp_words_by_ref -n : cur prev
    nb_colon=$(grep -o ":" <<< "$COMP_LINE" | wc -l)

    COMPREPLY=( $(compgen -W '$(hello --compbash --compgen "$((COMP_CWORD - (nb_colon * 2)))" "$prev" "${COMP_LINE}")' -- "$cur") )

    __ltrim_colon_completions "$cur"
  }
  complete -F _hello_completion hello
elif type compctl &>/dev/null; then
  _hello_completion () {
    local cword line point si
    read -Ac words
    read -cn cword
    read -l line
    si="$IFS"
    if ! IFS=$'
' reply=($(hello --compzsh --compgen "${cword}" "${words[cword-1]}" "${line}")); then
      local ret=$?
      IFS="$si"
      return $ret
    fi
    IFS="$si"
  }
  compctl -K _hello_completion hello
fi
### hello completion - end ###
Noted this sort of feature lacking with babashka (https://github.com/babashka/cli/discussions/42) but maybe something with this could be recycled to work there also.

đź‘€ 1
maleghast 2025-03-17T16:03:01.921389Z

Hi All… So I am trying to use figlet in an nbb app to create an ASCII Art Banner for the help page. Figlet is a node library that returns a promise which when resolved returns the relevant string, but I cannot for the life of me get the promise to resolve, even though I am using promesa in my app. Here’s the function I’ve written to wrap it up so I can call it:

(defn get-banner
  [banner-message] 
   (p/let [banner (figlet/textSync banner-message)]
     banner))
and here is where I am trying to use the banner:
(defn help-message
  "Function to display help and other on-invocation messages"
  [mode]
   (cond
     (= mode "help") (println
                      (get-banner "BIRTHDAYMAN - SQLITE")
                      "\n"
                      (str (fs/readFileSync (str
                                             (script-loc)
                                             "/help.txt"))))
     (= mode "invalid") (println (str (fs/readFileSync (str
                                                        (script-loc)
                                                        "/invalid.txt"))))
     :else (println "Unknown Help Mode")))

dpsutton 2025-03-17T16:04:53.109489Z

you can’t just pretend async is sync

dpsutton 2025-03-17T16:05:11.313429Z

get-banner is asynchronous. you can’t just do that in the mode help println

dpsutton 2025-03-17T16:07:12.087179Z

put the p/let in the cond and print from the promise resolution.

maleghast 2025-03-17T16:14:16.239929Z

I tried that, but that just breaks the whole thing.

maleghast 2025-03-17T16:15:14.035559Z

There has to be a way of forcing the promise to resolve, after all it seems insane that this returns a promise at all, but I guess that’s down to JavaScript..?

maleghast 2025-03-17T16:20:27.837429Z

I have other uses of p/let in the app that behave the way that I expect, i.e. the value of the resolved promise is what I get back when I try to do something / anything with the returned value.

maleghast 2025-03-17T16:28:44.772619Z

Also, I am (attempting) to use the synchronous version of the call figlet/textSync

maleghast 2025-03-17T16:29:40.997739Z

So I am even more confused as to why this promise-hell is causing me problems.

dpsutton 2025-03-17T16:32:09.035979Z

get banner returns a promise. there is no way to wait in javascript

dpsutton 2025-03-17T16:32:21.956349Z

you have to register a function to run when the value is ready

dpsutton 2025-03-17T16:32:41.712889Z

that’s what p/let is doing for you. so no way that the println can just synchronously get the return value

maleghast 2025-03-17T16:03:46.757439Z

What I keep getting on the command line is:

❯ node index.mjs help
#<Promise[pending:6]> 
 ***************************************************************************************************************
- run the app to add a birthday:

node index.mjs

- add the following arguments for other commands:

help (see this help)
list (see today's birthdays)
list-people (see a complete list of stored birthdays)
update (update a gift idea for a specific person)
delete (delete a person from the list)
search-day [day] [month] e.g. search-day 10 May (search for birthdays on
                                                 a specific day)
search-month [month] e.g. search-month May (search for birthdays in a
                                            specific month)

If you append any other argument it won't work.

***************************************************************************************************************
when I am expecting to get the ASCII Art banner at the top of this output.

borkdude 2025-03-17T16:31:03.388409Z

> to resolve the promise before returning it. you should probably handle the returned promise properly.

maleghast 2025-03-17T16:32:15.327079Z

OK, then that’s what I don’t understand. I have used p/let in other places in my code and that works the way I expect, so I am really at a loss. I expect I am doing something monumentally dumb, but this has been driving me nuts all weekend.

borkdude 2025-03-17T16:33:42.883989Z

p/let doesn't resolve promises, it just chains promise operations there is no synchronous blocking operation in JS

borkdude 2025-03-17T16:34:17.233119Z

so you should handle the returned promise of p/let and chain that with any other operations you might want to do after it using promise stuff, not sync stuff

maleghast 2025-03-17T16:39:10.304069Z

I am now certain I am being dumb, because you’ve explained that in a way that seems entirely natural and “common sense” to you and I have no idea what you mean. I just want the string - frankly I would love it if there was a library that made ASCII Art for me that just returned it instead of a promise, which seems way over engineered to me. I am completely lost, I cannot fathom out how to get the result and use it, and I am struggling to understand how I would do that by “using promise stuff”

borkdude 2025-03-17T16:41:09.980439Z

is your code public?

maleghast 2025-03-17T16:41:32.515709Z

Hold on, it can be, I could commit and push it broken ’cos it’s on a branch

maleghast 2025-03-17T16:41:36.026229Z

one sec…

borkdude 2025-03-17T16:41:38.479949Z

sure

maleghast 2025-03-17T16:44:12.075989Z

app.cljs line 268

borkdude 2025-03-17T16:44:25.749939Z

Alright, so this function:

(defn get-banner
  [banner-message] 
   (p/let [banner (figlet/textSync banner-message)]
     banner))
is basically the same as just not using p/let and directly returning the figlet call

maleghast 2025-03-17T16:45:09.474359Z

ok…

borkdude 2025-03-17T16:45:21.309749Z

in help-message you should do something like this:

(p/let [txt (get-banner)]
  (println txt))

maleghast 2025-03-17T16:45:55.998399Z

ok one sec

borkdude 2025-03-17T16:46:30.042269Z

or you could do something like this:

(p/let [txt (get-banner)]
  (help-message txt))
if you want to make the text an argument of help-message

maleghast 2025-03-17T17:01:29.676559Z

OK, I think that I get what you are driving at - I will try and re-work it so that the banner is an argument of the help function, and see where I get to. Thanks.

maleghast 2025-03-17T17:41:12.677959Z

If you refresh the branch I’ve pushed a change… It does now print out the banner, but then the section that is supposed to read in the file with the help instructions craps out massively.

maleghast 2025-03-17T17:43:53.974909Z

If I run it in the REPL (Calva) I don’t get the insane stack trace, but I do get this after the banner:

#<Promise[rejected:6]>

maleghast 2025-03-17T17:52:25.009029Z

And now I’ve had to leave to run my son back to his Mum’s - thanks for the help thus far, just did not want you think I was ignoring you if you posted anything further.

borkdude 2025-03-17T20:06:27.733499Z

Do you mean with "craps out massively" that you are seeing something like this?

> fs.readFileSync("README.md")
<Buffer 21 5b 43 6c 65 72 6b 3a 20 4c 6f 63 61 6c 2d 46 69 72 73 74 20 4e 6f 74 65 62 6f 6f 6b 73 20 66 6f 72 20 43 6c 6f 6a 75 72 65 5d 28 68 74 74 70 73 3a ... 5973 more bytes>

borkdude 2025-03-17T20:07:02.074609Z

The solution is to add "UTF-8" as the second arg:

fs.readFileSync("README.md", "UTF-8")

maleghast 2025-03-17T20:10:31.369399Z

Actually no, it stopped being able to find the file. But I am going to seriously consider adding the encoding to those calls. All I did was (def ….) the strings at the top of the file and not build them in the (cond …) inside the help message function and it started working again. Thanks for the insights - I have now got the app behaving as I wanted to, and it’s thanks to your suggestion, of building the banner in a prior function that then runs the final function, which I assume is forcing the promise(s) to resolve by crossing function boundaries..?

maleghast 2025-03-17T20:10:55.093839Z

(I realise that could be very wrong and it’s something else, but that’s whay my intuition is telling me about the pattern that works)

borkdude 2025-03-17T20:12:11.214089Z

I suggest you would look at macroexpansions of p/let to see what it actually does. Also a promise tutorial (without async/await) might help a bit to understand what's going on.

maleghast 2025-03-17T20:12:59.430079Z

I think that is what I will start with tomorrow.

borkdude 2025-03-17T20:13:32.673979Z

you could also try to write it without promesa, which would be educational

borkdude 2025-03-17T20:15:16.089849Z

E.g. this is how you would do it without promesa:

(def promise-that-resolves-to-number (js/Promise.resolve 1))

;; this whole thing is a value that resolves to... ?
(-> promise-that-resolves-to-number
    (.then (fn [value]
             (prn :the-value value)
             (inc value)))
    (.then (fn [value]
             (prn (= 2 value))
             (inc value))))

borkdude 2025-03-17T20:15:36.126499Z

which gets tiring quickly but at least you see what happens

borkdude 2025-03-17T20:15:43.318559Z

once you're "in" a promise, you can't escape it

borkdude 2025-03-17T20:15:52.249429Z

it's basically a monadic thing

maleghast 2025-03-18T13:13:29.270599Z

I had stepped away from the laptop and the internet when you posted this last thought, but I just wanted to say thanks. It’s still a little confusing, and thus I am going to invest some time and effort in learning more and getting my head around it, but I really appreciate the explanation. I have modified my app and refactored the modification so that the code is a lot nicer than it was originally, and has far less repetition as well, so thanks also for the interim support that empowered that. I actually use the app - my .zshrc on all my machines runs a search-month against my database of birthdays whenever I launch a new terminal, so it’s really pleasing to have it working the way that I wanted.

borkdude 2025-03-18T13:13:57.899519Z

👍

maleghast 2025-03-17T16:04:32.478399Z

and I just don’t understand why p/let in the little wrapper function is not doing what I expect it to - to resolve the promise before returning it.