Fork me on GitHub
#beginners
<
2020-07-06
>
johnjelinek00:07:13

does that mean to not use :gen-class :implements?

seancorfield00:07:50

:gen-class is a last resort.

johnjelinek00:07:01

oic ... I've been using it as a first resort

seancorfield00:07:24

Do you have a doc link for the RequestHandler interface?

seancorfield00:07:01

I think in a decade of Clojure, I've used :gen-class maybe twice?

seancorfield00:07:02

(reify RequestHandler
  (handleRequest [this input context ...))

seancorfield00:07:34

That will create an anonymous instance of a class that implements RequestHandler.

johnjelinek00:07:51

so, handleRequest is the entrypoint, so does the reify need to go in the defn of handleRequest?

seancorfield00:07:53

No. That reify call gives you back an object that implements the interface and has a single method, handleRequest that can be called by whatever you pass that object to.

johnjelinek00:07:35

(ns handler.core
  (:gen-class
   :implements [com.amazonaws.services.lambda.runtime.RequestHandler])
  (:import (com.amazonaws.services.lambda.runtime.events SNSEvent)))

(defn -handleRequest [event context]
  (println event)
  (println context))
this is what I have ... so, I need to maybe change the signature of handleRequest and have it call something that does the reify?

johnjelinek00:07:57

or can I call reify in the :implements [,,,] ?

seancorfield00:07:25

You don't need any of that.

seancorfield00:07:11

Wait... Explain how you're trying to use this?

seancorfield00:07:49

How/where are you creating the instance of this?

johnjelinek00:07:07

this is supposed to generate a jar and the class exposes handleRequest that gets called upstream by another process

seancorfield00:07:41

That doesn't answer my question.

johnjelinek00:07:01

uhh ... maybe I'm not using the right words. So, if I don't reify, I can usually do this and it works, but then I'll have to parse the json payload:

(ns handler.without.reify
  (:gen-class
   :implements [com.amazonaws.services.lambda.runtime.RequestStreamHandler])
  (:require [ :as io]))

(defn -handleRequest [this input-stream output-stream context]
  (let [w (io/writer output-stream)]
    (.write w "testing")
    (.flush w)))
☝️ this works, but I wanted to use the event objects provided as well

johnjelinek00:07:35

so I make a jar out of that, hand it to AWS and they have a thing that calls it

seancorfield00:07:53

Ah, so there's no Clojure code involved outside this?

seancorfield00:07:42

Ah, OK. Then, yes, you have to use :gen-class here. Sorry. Your caller is Java -- it creates the instance -- so you can't use reify here.

johnjelinek00:07:09

ok, thx for the clarification -- so, without reify, what am I to do to implement the interface with the generic?

seancorfield00:07:23

Generics don't exist at runtime.

seancorfield00:07:35

They are erased by the Java compiler.

johnjelinek00:07:14

ok, so, is it fine to tell it that it implements the interface, and then I can cast the event in handleRequest function?

seancorfield00:07:01

No need to cast.

johnjelinek00:07:36

then how do I use event as an SNSEvent ?

(defn -handleRequest [event context]
  (println event)
  (println context))

seancorfield00:07:13

Just call methods on it.

seancorfield00:07:27

To avoid reflection, you might want a type hint on the event argument.

johnjelinek00:07:49

lemme lookup type hints

seancorfield00:07:22

Just ^SNSEvent event

seancorfield00:07:32

in the argument list

johnjelinek00:07:39

noice!

(defn -handleRequest [^SNSEvent event context]
  (println event)
  (println context))

johnjelinek00:07:46

ya, I think that's what I was after, thank you!

seancorfield00:07:16

You've already imported the class so, yeah. That just avoids reflection in interop calls.

👍 3
johnjelinek00:07:13

and I think it makes it more readable

seancorfield00:07:18

Life is so much easier when your main entry point is Clojure 🙂

johnjelinek00:07:20

I'm getting this error now:

{
  "errorMessage": "Class handler.core does not implement RequestHandler with concrete type parameters"
}

johnjelinek00:07:58

looks like even with the type hints, it still bombs out

seancorfield00:07:11

Did it work without the type hint(s)?

seancorfield00:07:37

(I assumed you already had something working before?)

johnjelinek00:07:28

the only thing I had working before was where I'm not implementing this interface and parsing the json from the input stream

johnjelinek00:07:04

this works, but it's implementing RequestStreamHandler instead of RequestHandler

seancorfield00:07:16

#aws-lambda may be a better place for you to find help -- since you're specifically dealing with issues caused by AWS Lambda, right?

johnjelinek00:07:33

I can ask there, thanks!

johnjelinek00:07:49

note: with or without the type hints, it didn't work

seancorfield00:07:58

I've no idea what that error message means -- so I assume it's specific to AWS.

johnjelinek00:07:24

I think that error indicates that the interface didn't fill in the generics

seancorfield00:07:01

Generic types don't exist at runtime.

johnjelinek00:07:19

perhaps I can reify an implementation in another namespace and tell gen-class to implement that?

ghadi00:07:31

AWS Lambda interop is the worst

ghadi00:07:13

Static language doing a dance to be dynamic to resolve a static method implemented in a dynamic language

johnjelinek00:07:02

@ghadi: have you had success doing what I'm trying to do?

ghadi00:07:25

Implement a lambda?

ghadi00:07:37

I haven’t dug through the scrollback

johnjelinek00:07:40

implement the interface with generics using genclass

ghadi00:07:55

There’s no such thing as generics

seancorfield00:07:08

(that's what I keep saying)

👍 3
ghadi00:07:13

The RequestStreamHandler interface?

johnjelinek00:07:19

bump, interface followed by java implementation

johnjelinek00:07:34

(and I believe you 🙂)

seancorfield00:07:40

This

public class HandlerCloudFront implements RequestHandler<CloudFrontEvent, String>{

seancorfield00:07:54

(or similar, right?)

ghadi00:07:46

I never implement RequestHandler, I always implement RequestStreamHandler, then read the event as JSON

johnjelinek00:07:26

me too, that's what I usually do

ghadi00:07:34

the service specific events (SNSEvent) don't really give you anything over a map

johnjelinek00:07:35

but I wanted to see if this was possible

ghadi00:07:05

I haven't used aws-lambda-java-events from clojure

ghadi00:07:21

I don't see the benefits over receiving a map

johnjelinek00:07:09

ok, I'll go back to that

seancorfield00:07:52

Sometimes the best solution to a problem is to avoid the problem 🙂

ghadi00:07:48

java classes are artisanal maps

😆 3
Frosku01:07:47

https://github.com/Frosku/disultory Baby's first public release, tear it apart plz.

e02:07:51

i started poking around with websockets using sente. i ripped off quite a bit of code from sente’s examples. i noticed there are a few methods like this in both clojure and clojurescript:

defmulti -event-msg-handler
  "Multimethod to handle Sente `event-msg`s"
  :id ; Dispatch on event-id
  )

(defn event-msg-handler
  "Wraps `-event-msg-handler` with logging, error catching, etc."
  [{:as ev-msg :keys [id ?data event]}]
  (-event-msg-handler ev-msg))

(defmethod -event-msg-handler
  :default ; Default/fallback case (no other matching handler)
  [{:as ev-msg :keys [event]}]
  (ws-utils/->output! "Unhandled event: %s" event))

(defmethod -event-msg-handler :chsk/state
  [{:as ev-msg :keys [?data]}]
  (let [[old-state-map new-state-map] (have vector? ?data)]
    (if (:first-open? new-state-map)
      (ws-utils/->output! "Channel socket successfully established!: %s" new-state-map)
      (ws-utils/->output! "Channel socket state change: %s"              new-state-map))))

(defmethod -event-msg-handler :chsk/recv
  [{:as ev-msg :keys [?data]}]
  (ws-utils/->output! "Push event from server: %s" ?data))
i understand the destructuring going on here with {:as ev-msg :keys [blah blah blah]}, but is there any reason you might want to keep {:as ev-msg} in methods where it isn’t even being used (e.g., the :chsk/recv one), as opposed to simply [{:keys blah blah}]? it doesn’t seem to matter in practice when i change it up. i’m assuming that it’s either a) keeping the signature of the function uniform due to defmulti out of convention, or b) the code isn’t as clean as it could be. my linter is upset, so i’m inclined to remove it, but wanted to double-check that i’m not violating actually good code practice.

Alex Miller (Clojure team)02:07:58

it's useful to point you at the context of the overall map, not required

Alex Miller (Clojure team)02:07:13

linter will prob be happier with _ev-msg

Alex Miller (Clojure team)02:07:46

leading (or only) _ is often used as a convention to mean "not used"

e02:07:51

oh, cool, i didn’t know that trick. yes, it is happier now 🙂

Alex Miller (Clojure team)02:07:13

only a convention, has no meaning to Clojure, but linters follow it

👍 3
adam03:07:11

How can this be said with #() shortcut: (repeatedly (fn [] true))

dpsutton03:07:51

(repeatedly 3 (constantly true)). I don't believe you can use the #(true) syntax: (macroexpand (repeatedly 3 #(true)))`

dpsutton03:07:03

> (macroexpand `(repeatedly 3 #(true))) check this expansion to see why

adam03:07:58

Ah I was close (take 2 (repeatedly #(constantly true))) lol. Thanks.

dpsutton03:07:54

but if you just need a literal value there's (repeat 2 true)

dpsutton03:07:16

(actually not just literal value, just a value. sorry about that)

adam03:07:13

Got it, thanks. I actually need an infinite lazy seq as it’s part of a larger lazy structure

dpsutton03:07:45

ah, then (repeat true) is your friend

adam03:07:46

That’s fantastic. I didn’t realize (repeat) itself yields a lazy seq

dpsutton03:07:28

(doc repeat). i wish as a beginner i would have used doc and apropos more. they are truly fantastic

👌 3
Rama03:07:07

Hi, can anyone help me by referring a good article for using MSAL ( Azure AD B2C) with Clojure please.

noisesmith04:07:06

for something like this, you need to use interop

noisesmith04:07:40

if it works with java, you can use clojure with its java interop. if not, ther'es also cljs which can use the js interop

Rama04:07:15

Thank you. I will try those suggestions. appreciate your response.

Rama03:07:46

I have just started on Clojure few days ago. and struggling to get this info.

Walt Stoneburner12:07:44

Insidious problem: How can I get exception messages happening in threads to the console? As we're in the #beginners channel, here's a few more details as it's quite possible I'm asking the wrong question. Last night, I lost quite a few hours where a seemingly innocent six-line change completely stopped my software from behaving correctly – it didn't crash, it just acted like some function deep in the system was mysteriously no longer getting called. Thanks to git, I narrowed my way in on the problem. It was a stupid mistake on my part, I was trying to pass a date and had an extra set of parenthesis, so it was trying to execute the value... the kind of thing you'd get a "cannot convert to class clojure.lang.IFn" exception for. Except Clojure was showing me nothing. No exceptions. No errors. Nada. Deeper dive: I'm using websockets, specifically ztellman's aleph. When a socket makes a request, a callback handles it, constructs the response, and returns it to the socket. All this is working fine, and my trivial error was in one of the callback helper functions. In hind-sight, and I can only reason about the behavior, it acted like the thread crashed (silently) due to an exception. I just had no way of knowing that was the issue, much less where or why. What I was expecting was an exception message on the console, but I'm not quite clear what's truly happening. Perhaps aleph has background threads, maybe it's doing something with futures, maybe there's dark magic. I'd suspect if stdout/stderr was involved, it'd inherit those values from the main thread, but then again isn't exception reporting the role of the VM, so I'd think that shouldn't matter. Environmentally, I was running IntelliJ 2020.1 with Cursive 1.9.2-2020.1 as the REPL. I feel like I'm missing something pretty essential and basic. :thinking_face:

Walt Stoneburner12:07:37

I've also been wondering if alepha is holding a thread open for each websocket connection, or whether it acts more like the good old select() system call that allows one thread to multiplex socket activities, similar to NodeJS.

Walt Stoneburner12:07:51

This certainly sounds like what's happening -- although it's aleph that's doing the magic at the time a socket connects to the web server, so I'm not exactly sure how I get this to happen at thread creation time. But this does give me something to research. Still accepting ideas and suggestions.

jsyrjala12:07:55

You register the global exception handler once, e.g. at start of your program

Walt Stoneburner12:07:36

I was just looking at https://groups.google.com/forum/#!topic/aleph-lib/suDg8bbxnSo, which shows it's possible with Alpha, and just got back here to post the link when I saw your message @U1G5P6G0L. Can you point me at a resource on how to do that with Clojure? #beginners

jsyrjala12:07:03

(Thread/setDefaultUncaughtExceptionHandler (reify UncaughtExceptionHandler (uncaughtException [this thread ex] (println "Got Exception" ex "from thread" thread"))))

jsyrjala12:07:10

something like that

Walt Stoneburner14:07:01

(status so far) -- the code from the article above, which closely matched the code fragment shown by @U1G5P6G0L, compiled fine but appeared to have no effect when I reintroduced the simple error. I did attempt throwing an Exception manually, and that was caught and appeared on the console ...but not by the exception hook I installed. :researching: (Want to make sure it isn't user error.) That said, this is the first time I'm seen a stack trace with an io.aleph.dirigiste.Executor$Worker$1.run and a manifold.executor/thread-factory/reify/f in it.

jakubl15:07:31

One of the issues I have with the concept of exceptions is that it leads people into thinking that errors are exceptions - if you are concurrently processing data - which presumably involves some I/O at some point - something will fail - it's not a matter of if - it's a matter of when. Best to build your code around it from the start. I would think unittests and spec are your friends. I have also been using this to turn my errors into values rather than to have exceptions jump around my code - https://github.com/fmnoise/flow

Walt Stoneburner16:07:22

@USWSAE547 - preach! And while I'm "pickin' up what you're puttin' down," the code I'm working against does not use Exceptions to catch errors; in fact, that's why there is no try/catch going on anywhere. But in this case unittests and specs aren't gonna catch dumb coding mistakes like where I accidentally had an extra set of parenthesis around a value. Given that's caught at runtime, I want a hard crash to make me go resolve the problem. Unless I can get the Exception to surface on a thread, it'll silently just start working incorrectly. I'm facing a new problem that the global UncaughtExceptionHandler is not being triggered -- which makes me wonder if something in the library or about the invocation methodology is catching the error. : still investigating :

jakubl18:07:47

Fair - I found unittesting each component of processing pipeline - was critical to my success - I am also a fan of random errors - that you can throw in unittests and see how well your code copes when things randomly break - but yeah I have yet to find a remedy for mistakes - still looking though 🙂

Alex Miller (Clojure team)12:07:24

In the JVM, every thread can have its own uncaught exception handler (set via the Thread api) and there is also a default uncaught exception handler, which should print exceptions to stderr.

Alex Miller (Clojure team)12:07:16

so in the default setup, you should see thrown exceptions on background threads in your console

Alex Miller (Clojure team)12:07:41

I'm not sure what aleph or other libs you might be using do though

Walt Stoneburner12:07:51
replied to a thread:Insidious problem: *How can I get exception messages happening in threads to the console?* As we're in the #beginners channel, here's a few more details as it's quite possible I'm asking the wrong question. Last night, I lost quite a few hours where a _seemingly_ innocent six-line change completely stopped my software from behaving correctly – it didn't crash, it just acted like some function deep in the system was mysteriously no longer getting called. Thanks to git, I narrowed my way in on the problem. It was a stupid mistake on my part, I was trying to pass a date and had an extra set of parenthesis, so it was trying to execute the value... the kind of thing you'd get a "cannot convert to class clojure.lang.IFn" exception for. Except Clojure was showing me _nothing_. No exceptions. No errors. Nada. Deeper dive: I'm using websockets, specifically ztellman's *aleph*. When a socket makes a request, a *callback* handles it, constructs the response, and returns it to the socket. All this is working fine, and my trivial error was in one of the callback helper functions. In hind-sight, and I can only reason about the behavior, it acted like the thread crashed (silently) due to an exception. I just had no way of knowing _that_ was the issue, much less _where_ or _why_. What I was expecting was an exception message on the console, but I'm not quite clear what's truly happening. Perhaps aleph has background threads, maybe it's doing something with futures, maybe there's dark magic. I'd suspect if stdout/stderr was involved, it'd inherit those values from the main thread, but then again isn't exception reporting the role of the VM, so I'd think that shouldn't matter. Environmentally, I was running IntelliJ 2020.1 with Cursive 1.9.2-2020.1 as the REPL. I feel like I'm missing something pretty essential and basic. :thinking_face:

This certainly sounds like what's happening -- although it's aleph that's doing the magic at the time a socket connects to the web server, so I'm not exactly sure how I get this to happen at thread creation time. But this does give me something to research. Still accepting ideas and suggestions.

Alex Miller (Clojure team)12:07:19

the classic MO of async processing is to have a bug that doesn't send something along when it's supposed to and processing just stops - this can happen without an exception, just with faulty logic. this is kind of the classic tradeoff of splitting your effective stack trace across a bunch of independent async processes

👍 3
deg12:07:17

I'm ready to start a new project in CLJS, after a couple of years away from Clojure. Back then, my tools of choice were leiningen and re-frame. Today, for a browser app, making heavy use of existing JS and React libraries, what's the recommended tooling? Still the same, or shadow-cljs + ???, or ....?

gekkostate14:07:44

I usually go with shadow-cljs, reagent and re-frame.

deg14:07:37

Thanks. If I'm reading the current re-frame-template readme correctly, it looks like this is already built-in?

gekkostate15:07:56

Yep, it’s already built in.

Michaël Salihi16:07:06

Hello ! I just read this post find on Twitter "How to check if a list contains a value in Clojure" https://www.mednikov.tech/how-to-check-if-a-list-contains-a-value-in-clojure/ Is there more idiomatic solutions ? I'm surprised, it seems a little bit complected to return true or nil/false if list contains or not an value.

noisesmith16:07:40

the idiomatic solution is to use an indexed collection by the value you want to look up (eg. a set or a map with the items you want to check for as keys)

noisesmith16:07:59

using some is a fallback and doing it frequently is a sign you are using the wrong collection type

3
noisesmith16:07:34

Also that author is being sloppy with terms that are important (especially important as a beginner) - methods and functions both exist, contains? is a function, it's not a method. A function is an Object implement IFn, which includes an invoke method, when clojure compiles (f x) it looks for the invoke method on f, and compiles code calling that method on x

noisesmith16:07:27

this is important because methods do not exist as first class values (you can use reflection to get method handles, but those still are Objects standing for a method in a reflective context). you can pass a function as an argument, because it's an object, you can't pass a method as an argument, because it isn't, it has no standalone existence apart from the object it belongs to

Michaël Salihi16:07:38

Perfect @U051SS2EU for all this explanations, thx!

noisesmith16:07:56

np - hope it wasn't too much of a brain dump

gerritjvv14:07:49

I read this article on devzone.

gerritjvv14:07:04

made this https://gist.github.com/gerritjvv/15c721c0e1eac18cad953120975d3968 to show different ways to look for values in a list.

👍 3
Frosku19:07:43

Really weird one: for some reason in my test namespace, clj-http isn't decoding JSON, but in my -core namespace, it is. This makes testing basically impossible.

seancorfield19:07:23

Sounds strange. You're providing the same options in both calls?

seancorfield19:07:09

Are you hitting the same service, or is the test code hitting a different endpoint? (maybe the test endpoint has the wrong content-type so clj-http thinks it isn't JSON?) @U016JPB0Z27

Frosku19:07:43

The test endpoint has "Content-Type" "application/json" in the header.

Frosku19:07:54

It's using clj-http-fake.

fappy19:07:49

If a function returns a future that no one uses, the computation inside the future still will take place, right? That is, a future can be a way of saying “I want these things to happen on some other thread” and I can choose to deref the future if I want the result (or not deref it)?

seancorfield19:07:05

@fappy Yes, future can be used for "fire-and-forget" tasks.

seancorfield19:07:59

It's worth thinking about exceptions -- an exception in a future will be "lost" unless you either dereference it or have a try / catch inside the future (and log the failure or do something else with it)

✔️ 3
Travis Jungroth19:07:49

Hi everyone! I'm just starting out in Clojure. I already have a question that I'm too curious about to wait until I run into an answer. What's the Clojure substitute for duck typing? I'm trying to figure out how you delegate calculations. For example, what's the Clojure equivalent of this Python code?

class Circle:
    def __init__(self, length):
        self.length = length

    def area(self):
        return (self.length / 2) ** 2 * 3.14  # close enough


class Square:
    def __init__(self, length):
        self.length = length

    def area(self):
        return self.length ** 2


total_area = sum([shape.area() for shape in [Circle(4), Square(4)]])
print(total_area)

dpsutton19:07:08

lots of ways to do polymorphism in clojure. One example would be

(defmulti area :shape)
(defmethod area :circle [{:keys [radius]}] (* Math/PI radius radius))
(defmethod area :square [{:keys [length]}] (* length length))


(area {:shape :circle :radius 3})

phronmophobic19:07:17

@U017ANLQXBJ, this is also possible with protocols:

(defprotocol IArea
  (area [this]))

(defrecord Circle [radius]
  IArea
  (area [this]
    (* Math/PI radius radius)))

(defrecord Square [length]
  IArea
  (area [this]
    (* length length)))

(area (->Circle 2)) ;; 12.566370614359172
(area (->Square 2)) ;; 4
Clojure has pretty flexible dispatch mechanisms. You may be interested in these articles that talk about the CS theory as well: https://en.wikipedia.org/wiki/Dynamic_dispatch https://en.wikipedia.org/wiki/Multiple_dispatch

phronmophobic20:07:19

since clojure has an "a la carte" approach to OO and polymorphism, I unexpectedly learned a lot about OO from clojure even though clojure isn't primarly an OO language

stopa19:07:29

noob reify question:

(defn firebase-save [path v]
  (-> (FirebaseDatabase/getInstance)
      (.getReference path)
      (.setValueAsync
        (stringify-keys v)
        (reify DatabaseReference$CompletionListener
          (onComplete [err _ref] (throw err)))))
Goal is to have firebase throw the actual error when saving value (rather then a java.util.concurrent.Exception) I am trying to reify DatabaseRefrence$CompletionListener , but am getting the error; Can't define method not in interfaces: onComplete Am not sure why clj is telling me ^ -- onComplete does seem to exist on the interface > https://firebase.google.com/docs/reference/android/com/google/firebase/database/DatabaseReference.CompletionListener (when i click into it from intellij, I see it exists too. How could I dig deeper?

ghadi19:07:44

@stopachka from the reify docstring: > Note that the first parameter must be supplied to correspond to the target object ('this' in Java parlance) Thus methods for interfaces will take one more argument than do the interface declarations.

❤️ 3
ghadi19:07:23

the error is suggesting that there isn't a method with that exact signature

ghadi19:07:35

need [this err ref] as the args

ghadi19:07:58

(deftype, reify and defrecord all behave this same way)

stopa20:07:35

Awesome, works like a charm, thanks @ghadi!

stopa20:07:54

One more noob question:

(defn firebase-save [path v]
  (future
    (-> (FirebaseDatabase/getInstance)
        (.getReference path)
        (.setValue
          (stringify-keys v)
          (reify DatabaseReference$CompletionListener
            (onComplete [_this err ref]
              (if err
                ;; TODO future should throw? 
                (throw err)
                ;; TODO future should complete?
                true)))))))
Goal: firebase gives me an onComplete handler. If there's an error, I want to throw the original error to the future, otherwise I want to complete it Here I would guess the future would complete without waiting for onComplete How could I do above?

noisesmith20:07:00

you could create a promise, then put a deliver to that promise inside the onComplete method

noisesmith20:07:30

that's cheap, and gives you an object that will block on deref (and return false for realized?) until that onComplete runs

noisesmith20:07:53

after which it will return the value delivered in the onComplete on deref

noisesmith20:07:29

if you want the future not to complete until the onComplete returns, you could put a deref of the promise as the last thing in the future

stopa20:07:31

(defn firebase-save [path v]
  (future
    (let [p (promise)
          _work (-> (FirebaseDatabase/getInstance)
                    (.getReference path)
                    (.setValue
                      (stringify-keys v)
                      (reify DatabaseReference$CompletionListener
                        (onComplete [_this err ref]
                          (deliver p [err ref])))))
          [err ref] @p]
      (if err (throw err)
              ref))))
^ something like this?

noisesmith20:07:52

that looks right, yeah

stopa20:07:46

bam, thanks!

stopa20:07:27

note: reason I am wrapping with future, rather then just returning promise, is so I can get the semantics, that by dereferencing an error, it would throw

stopa20:07:33

^ is this idiomatic, or not quite?

noisesmith20:07:19

you could get the same thing, without "wasting" a thread, by reifying IDeref

noisesmith20:07:32

(using a promise to implement, throwing on some condition)

stopa20:07:56

Oo interesting!

ghadi20:07:00

you could also turn the completion notifs into data on a core.async channel

noisesmith20:07:15

foo=> (def d (reify clojure.lang.IDeref (deref [this] (if (> (rand) 0.5)
 (throw (Exception. "you lose")) 2))))
#'foo/d
foo=> @d
Execution error at finops_admin.auth$reify__52601/deref (48914f44106185aac2a591f0a62c0
f52e50e2fdfe7863baaeea7e53dc016c825-init.clj:1).
you lose

foo=> @d
2

stopa20:07:03

Okay a bit of a noob question: How would I merge this reify Idea, with promise? Maybe I can do something like: have a promise that accepts expects a delivery of [err res] in IDeref, if err we throw

noisesmith20:07:24

you would deref the promise and do the conditional in your original, inside the deref method

noisesmith20:07:17

(reify IDeref (deref [this] (let [[err ref] @p] (if err (throw err) ref)))

noisesmith20:07:22

or something similar

stopa20:07:08

oo! Very cool. Okay let me try (hacking together)

noisesmith20:07:47

since this is the beginners channel, I'll go into more detail - the deref method is what gets called when you use @, so here you are describing the behavior of this anonymous object if it is dereferenced

❤️ 3
stopa20:07:46

(defn throwable-promise [f]
  (let [p (promise)
        resolve #(deliver p [nil %])
        reject #(deliver p [% nil])
        throwable-p (reify IDeref 
                      (deref [this] 
                        (let [[err ref] @p] 
                          (if err (throw err) ref)))]
    (f resolve reject)
    throwable-p))

noisesmith20:07:07

oh, that's a nice way to encapsulate it

❤️ 3
kaneko20:07:44

Hi guys I just wanted to run a simple shell command using clojure. I am using the clojure.java.shell's sh function. When I run something like (sh "ls") this prints out what I expect in the REPL. However, when I run (sh "lsof" "-nti:" (str port)) it doesn't print anything out. I can hit enter again and I get back the REPL prompt. Does anybody know why this is?

kaneko20:07:28

Okay, I think it is because I've run another (sh ...) previously which is hogging the *stdout* . Once I killed that process... it seems to work.

noisesmith20:07:24

what does it return? you can get the last return value with *1

Alex Miller (Clojure team)20:07:38

should that be (sh "lsof" (str "nti:" port)) ?

noisesmith20:07:22

shell/sh should collect all stdout/stderr from the process as data

kaneko20:07:10

That might be it, I'll try that @alexmiller

kaneko20:07:32

That works. (I had to add the hyphen before the "nti" part) I think the main problem was that I ran an (sh ...) that started a server and that was hogging(?) all the *stdout* stuff. When I killed my server process, both the server's output and the "lsof" output were printed out in the REPL.

noisesmith20:07:17

I think the previous call was simply blocking your repl, sh doesn't run in the background

noisesmith20:07:59

it shouldn't be using `*stdout*` at all, it's redirected to a string

kaneko21:07:01

If it was blocking my repl, I shouldn't be able to run another command right? Or does a repl command get dispatched to a new thread?

noisesmith21:07:26

if it's blocking your repl, the repl doesn't read again until it returns

noisesmith21:07:57

you can still type in, and that will get echod back

noisesmith21:07:06

you just won't cause any evaluation

kaneko21:07:41

Ah, okay. I'll just run a test to double check!

kaneko21:07:18

Yeah! I am able to type things in but nothing gets evaluated!

noisesmith21:07:23

also, the ProcessBuilder and Process classes that sh is built on are not especially hard to use, and offer a lot of flexibility

kaneko21:07:32

Would they be the right way to start some long running process? I guess since process is in the name... 😛

Alex Miller (Clojure team)21:07:35

sh is not built on those

kaneko21:07:46

Thank you, I will take a look at those!

Alex Miller (Clojure team)21:07:33

it's built on the older and much less good Runtime stuff

noisesmith21:07:53

oh, my mistake

Alex Miller (Clojure team)21:07:00

I've been using ProcessBuilder via interop lately and they are much better :)

noisesmith21:07:36

yes - was actually amazed by how easy Process / ProcessBuilder were to use, easier than the most popular wrapper on them even

Alex Miller (Clojure team)21:07:14

(defn exec
  [command-args]
  (let [proc-builder (doto (ProcessBuilder. ^List command-args)
                       (.redirectOutput ProcessBuilder$Redirect/INHERIT)
                       (.redirectError ProcessBuilder$Redirect/INHERIT))
        proc (.start proc-builder)]
    (.waitFor proc)))

👍 3
Alex Miller (Clojure team)21:07:45

is a pretty good start for a lot of stuff (but doesn't handle swiping a "result" from the process, really just for forking out

ghadi21:07:03

In Java 9+, Process gives you a completion callback too. Very useful

noisesmith21:07:42

in a terminal this runs an editor, then you get your repl back after it exits

(-> ["vim"]
    (ProcessBuilder.)
    (.inheritIO)
    (.start)
    (.waitFor))

😮 9
Miguel Mejia23:07:17

Woaaah this is awesome

noisesmith21:07:18

if you remove the waitFor call, every other character you type goes to the repl or the editor on my machine

adam23:07:28

What is another way of saying:

(when (rand-nth [true false])
  "Some code")
I want to set the probability with a bit more precision. Say, have the code executed about 65% of the time.

noisesmith23:07:47

how about (when (< (rand) 0.65) ...)

adam23:07:00

Awesome, thanks