Tensegritics / ClojureDart

Clojure dialect for Flutter and Dart
1.41k stars 90 forks source link

Can't `with-open` #296

Open daveliepmann opened 10 months ago

daveliepmann commented 10 months ago

Describe the bug with-open will not compile.

Does your problem persist after clj -M:cljd clean && flutter clean? Yes

To Reproduce Steps to reproduce the behavior:

  1. Create any ClojureDart project. I reproduced with your first app on sha 303e2621391abaf52a7179efb848bf6d73ebb2e8 but it was also present on 6c1b1538310862dfe3fc3960fb897cf5de5a4b59 in another project
  2. Modify the source to include any with-open form. It breaks with any with-open form but the simplest I could invent was:
    1. add ["dart:io" :as io] to namespace :requires
    2. replace "Let's get coding!" message with (with-open [client (io/HttpClient)] (str client " foo"))
  3. See error:
    Compiling to Dart... @10:09:59
    acme.main
    Keep calm and fix bugs! šŸ‘‘
    Error while compiling with-open
    ā›”ļø Unknown symbol: with-open at line: 15, column: 13, file: acme/main.cljd
    Faulty subform and/or expansion with-open
    While compiling (defn main [] (f/run (m/MaterialApp .title "Welcome to Flutter" .theme (m/ThemeData .primarySwatch m.Colors/pink)) .home (m/Scaffold .appBar (m/AppBar .title (m/Text "Welcome to ClojureDart"))) .body m/Center (m/Text (with-open [client (io/HttpClient)] (str client "--foo")) .style (m/TextStyle .color m.Colors/red .fontSize 32.0))))

Expected behavior with-open should compile, create its bindings, and then .close the bindings

valerauko commented 7 months ago

I ended up "writing" my own with-open, but then I soon realized that half the things I wanted to with-open were actually closed with .cancel and not .close. And then some more had to be .dispose'd...

cgrand commented 7 months ago

And creating a closable protocol would require lots of tactical extensions. I suspect that with-open must stay true to its original spirit of relying on conventions and not interfaces. Something like?

(with-open [controller (xxx) :close .dispose] ...)
cgrand commented 3 months ago

I propose adding with-open to CLJD with the following deviations from CLJ: 1/ allow for keyword options in the bindings vector (options apply to the previous binding) 2/ only supported option is :close whose value is a form into which the resource is threaded (as per ->). Default value is .close. When the close is itself async, the user would have to write :close (-> .terminate await). Is it common enough to mandate an :await-close option? I don't think. 3/ allow destructuring in bindings (useful for example with StreamController where stream and sink fields have to be used). It doesn't make sense in CLJ where there's no :flds destructuring.

However with-open doesn't cover Dart streams. We need something like Dart's await for. There are at least two distinct macros/fns: one to reduce (should we expect the reducing function to return a future? If not should we provide two reduce variants? Should we also consider transduce? Or should we go the areduce way?) and one to perform side-effects (dostream to riff on doseq).

cgrand commented 3 months ago

Related #287