Fork me on GitHub
#clojurescript
<
2015-07-15
>
noisesmith00:07:02

markbillie: process management in the jvm is really sad, like there is no standard way to get the PID of a child process

noisesmith00:07:06

strange but true

noisesmith00:07:40

markbillie: I think some of us might be relying on the unix policy of reaping children that haven't been orphaned, but under Windows you have no such convenience

noisesmith00:07:05

(if that's even your platform)

mfikes02:07:36

Great work by @lazy-lambda — Replicator: A bootstrapped ClojureScript REPL for Android! http://tahmid.me/posts/2015-07-15-bootstrapped-cljs-repl-for-android.html

lazy-lambda02:07:44

I am so tired

lazy-lambda02:07:46

Will sleep now

lazy-lambda02:07:04

Thanks again !!!

mfikes02:07:09

Get some rest!

guy_do_or_die09:07:31

hello. i have a question about Hoplon. is there a good way to interpret html from text field inside a loop-tpl construction? thanks.

niwinz11:07:27

How is the best way a report an possible issue with the compiler api? (mainly related with cljs.build.api/watch and cljs.build.api/build)

niwinz11:07:00

I want to be sure if this is a bug before open a jira issue...

mfikes11:07:14

In the past discussions would often occur in IRC

mfikes11:07:01

Also, it is usually good to be able to repro a potential issue with a variation on Quick Start

mfikes11:07:57

What are you seeing?

niwinz11:07:38

I'm want to watch multiple directories using the watch function

niwinz11:07:01

The watch function interprets the first parameter using the Inputs protocol

niwinz11:07:09

with default implementation for File and String

niwinz11:07:36

The watch also forward the source parameter to the build function

niwinz11:07:02

and build is not aware of Inputs, instead of it, it works in terms of Compilable protocol

niwinz11:07:26

That has default implementation for URI String, List and Vector

niwinz11:07:59

so if I add implementation to Inputs protocol for Vector for watch multiple files,

niwinz11:07:23

The watch will works properly but the compilation not! Because the Compilable protocol has implementation for Vector that does not interpret the fector of files. Instead of this, it interpret a vector as list of expressions.

mfikes11:07:25

I think stuff surrounding multiple directories isn't really a bug—it is just the way it works today.

mfikes11:07:24

For example :analyze-path worked this way until recently.

mfikes11:07:12

(Referring to this JIRA in example above: http://dev.clojure.org/jira/browse/CLJS-1308 )

niwinz11:07:27

I Understand, but at this moment for the compilation and watching multiple directories I should use (b/watch (b/inputs "test", "src") ... instead of the most obvious way (b/watch ["test", "src"] ...

mfikes11:07:53

It sounds like an enhancement ticket.

niwinz11:07:13

Yes, I agree...

niwinz11:07:25

But at this moment the enhancement maybe implies breaking changes

niwinz11:07:48

because the build function already has implementation for vector that does not expect paths

mfikes11:07:06

Perhaps the enhancement could be filed. I think I see what you are asking now: “Where are potential strategies for compiler changes discussed?”

mfikes11:07:02

One pattern I've seen: A ticket is written, and then discussion happens (in the past in IRC) or proposed patches are added to the ticket and commented upon in the ticket. I suppose this process isn't described in the ClojureScript wiki (where the process isn't yet as formal as Clojure's).

mfikes11:07:51

Or, even before a ticket is written, sometimes a light discussion occurs as to whether an enhancement is itself reasonable. Like “would it make sense to watch multiple directories?”

niwinz11:07:12

Ok, I'll try to write a enhancement proposal later. Thanks @mfikes for your suggestions!

mfikes11:07:29

FWIW, I think there are places that work in terms of a single directory, and that may be for no other reason than nobody's yet asked for multi-directory support.

mfikes11:07:41

I also may be missing what you are saying: “The watch support API appears to support multiple directories, but it doesn't work.”

mcgivernsa11:07:16

I think he's saying it does work, it's just non-obvious

mcgivernsa11:07:35

(cljs.build.api/watch (cljs.build.api/inputs "src" "test") ...) works for me

mfikes11:07:18

Ahh. I missed that point. Thanks :). I'm being dense this morning. :)

mcgivernsa11:07:11

fwiw @niwinz I would like an enhancement proposal for this too

niwinz11:07:16

It works, but not how I expert to be works. If I do (cljs.build.api/build "src") it interprests "src" as path, but if I do (cljs.build.api/build ["src", "test"]) it interprets that as a vector of expressions instead of vector of paths.

mfikes11:07:18

One thing: the api namespace is supposed to act as a clean barrier so that things needn't be coupled to internal knowledge. It's docstring should clearly say how to watch multiple directories.

mfikes11:07:36

I just took a look at the API docstring for watch. It isn’t obvious that there is an inputs function defined earlier that pertains to watch.

frankiesardo12:07:02

@mfikes @lazy-lambda if I eval something simple as ":foo" it seems the "update" function inside the replete/read-eval-print is called twice: one with ":foo" and another with a "\n". Is this expected?

frankiesardo12:07:11

Could be the same issue

mfikes12:07:15

In other words, Replete isn’t yet properly dealing with the distinction between print and println for example.

frankiesardo12:07:49

Is there a reason why it has to call an "update" function internally, rather than just returning the evaluated string (that then the client can format itself)

mfikes12:07:30

@frankie: It actually used to do that initially. But then there are things like (do (print “hi”) (println “hello”))

mfikes12:07:02

(So, it is an open issue as to how Replete does the right thing in the end.)

frankiesardo12:07:43

Could it return a data structure with like {:result nil :out ["Hi" "Hi"]}

frankiesardo12:07:23

mhmh, no, that does not really work

frankiesardo12:07:45

Is there a way to pass out so we can bind it the replete/repl

dnolen15:07:13

@niwinz: if you want to pass multiple directories you must pass (cljs.build.api/inputs “src” “foo” …)

niwinz15:07:26

@dnolen I'm aware of it. Thanks! But if you read the rest of the conversations. The final issue is ... (cljs.build.api/build "src") interprets "src" string as path but if you pass a vector of strings it interprets them as expressions or something similar. And the issue is about the not obvious behavior.

dnolen15:07:54

@niwinz: it’s not going to change, that’s how it works

dnolen15:07:57

use inputs

niwinz15:07:21

ok! nice to know! This will avoid spend time to write a enhancement proposal 😄

dnolen15:07:32

happy to take docstring enhancements so it’s clearer to new users

bostonaholic16:07:48

is it expected that (.. element -dataset -fooId) does not preserve fooId for :advanced compilation?

bostonaholic16:07:26

^^ [org.clojure/clojurescript "0.0-3308"]

jonas16:07:53

@bostonaholic: it probably get’s minified by advanced compilation. You can do (aget (.-dataset element) “fooId”) instead

dnolen17:07:49

@jonas: I now recommend using (goog.object/get …) over aget

jonas17:07:06

@dnolen: interesting, why is that?

dnolen17:07:22

@jonas: lots of reaons, 1) objects aren’t arrays

dnolen17:07:37

but in the future there may be a lot more type inference baked into the compiler

bostonaholic17:07:46

@dnolen: yeah, that's what's happening and using aget works. I'm just wondering if it shouldn't get minified

dnolen17:07:47

and stuff like that is really not good style

dnolen17:07:13

@bostonaholic: pretty much anything can get minified

jonas17:07:20

@dnolen: ok, that’s good to know. I’ll try to remember that simple_smile

dnolen17:07:24

so you should not be surprised by this

dnolen17:07:41

I’ve pinned the page about Google Closure Advanced compilation

dnolen17:07:45

please read it carefully simple_smile

colin.yates17:07:59

@dnolen - forgive my ignorance but where is that page pinned? (newbie Slack user)

dnolen17:07:00

@colin.yates: click the “i” next to the member count at the top

dnolen17:07:05

you should see 5 pinned items

dnolen17:07:19

is the one I’m talking about

roberto17:07:59

didn’t know about that feature

meow19:07:28

Here's an odd question. I have a function that returns an atom. Inside the function a timer regularly updates the value of this atom. Should the name of the function end with a bang, or not? func-that-returns-atom! or just func-that-returns-atom?

chris_johnson19:07:34

let me paraphrase that back to you to make sure I understand what’s happening

gtrak19:07:52

my first question is why not return a chan of changes?

meow20:07:23

gtrak: are you just giving me grief because of my posts about core.asyn? simple_smile

chris_johnson20:07:44

you’ve got a function that returns an atom, presumably always the same atom, and every :timer-step seconds a loop inside the function swap!s the value in the atom?

meow20:07:49

@chris_johnson: well, a new atom each time, otherwise, yes - so you would just call it one time

chris_johnson20:07:00

so you’d expect caller A at time a to get an atom with value a’, and caller B at time b to get an atom with value b’, or would A’s atom also now hold b’?

chris_johnson20:07:29

I guess I’m struggling with the reason for returning an atom instead of just a calculated value based on the time

meow20:07:48

the original caller would hold onto the atom locally and just deref it to get the new value

chris_johnson20:07:30

oh, I understand now

chris_johnson20:07:31

I’m really new here and somewhat of a newbie in general, but I think I’d put at least one ! at the end of that method name tbh

chris_johnson20:07:39

you’re basically returning self-mutating state

chris_johnson20:07:00

you’ll want the antigrav lifters and a full-atmosphere hazmat suit to move that thing around

meow20:07:44

yeah, the weird thing is that the state I'm mutating is state that originates within the function, so kinda different

gtrak20:07:08

@meow: no grief simple_smile, just curious why complect the change state with a container holding the current value.

gtrak20:07:16

but i don't know your use-case

meow20:07:25

I'll post the code

meow20:07:33

(defn request-animation-frame
  "A delayed callback that pegs to the next animation frame."
  [callback]
  (.start (goog.async.AnimationDelay. callback)))

(defn measure-fps
  "Returns an atom containing the frames-per-second measured at regular intervals."
  ([]
   (measure-fps 500)) ; Measure every half-second
  ([interval]
   (let [fps (atom 0)
         frame-count (atom 0)
         starting-point (atom nil)]
     (letfn [(callback
              [timestamp]
              (request-animation-frame callback)
              (if-not @starting-point (reset! starting-point timestamp))
              (let [elapsed (- timestamp @starting-point)
                    f-count (swap! frame-count inc)]
                (if (>= elapsed interval)
                  (do
                    (reset! fps (->> (/ f-count elapsed) (* 1000) (.round js/Math)))
                    (reset! frame-count 0)
                    (reset! starting-point timestamp)))))]
       (request-animation-frame callback))
     fps)))

meow20:07:55

so it isn't actually a timer

meow20:07:18

just repeated calls to raf

gtrak20:07:49

rather than return an atom, you could take a callback

gtrak20:07:55

which would be more composable

meow20:07:58

I'm open to suggestions on better ways to handle this

gtrak20:07:47

@meow: I would use core.async go-loops instead of atoms for all of this, and like a sliding window over the last few intervals.

gtrak20:07:55

like keep a sum and a queue, subtract popped, add pushed, divide by a fixed number each time.

meow20:07:08

@gtrak: yeah, I guess I could write one that way - will probably do that just for the experience

meow20:07:28

this one seems to work well so far

meow20:07:07

on the consuming end of this all I want is a number that's magically updated in a way that doesn't have a negative impact on what it's measuring, of course, right!

gtrak20:07:39

Yea, i just don't use atoms that much, and i can see wanting to decouple the storage from the updates.

gtrak20:07:56

or you end up adding watches to the atoms and then you're back to callbacks again

meow20:07:23

I guess I would argue that this is kind of a special case

meow20:07:00

I sure don't want to take a callback as an argument as who knows what that function might do

gtrak20:07:35

I don't really get that rationale simple_smile

meow20:07:15

then again...

gtrak20:07:18

it becomes up to the caller to do the right thing

meow20:07:21

I'm thinking about it

gtrak20:07:23

not that function's problem

wildermuthn20:07:00

Was there a winner in the bootstrap namespace?

meow20:07:17

@wildermuthn: I think it was cljs.js

wildermuthn20:07:19

So I was thinking about use cases, particularly in atom-shell/electron. If I am building something that takes Clojure code as input, like an IDE, I see how useful that would be. But I’m curious about other uses.

wildermuthn20:07:48

Would macros become more powerful?

dnolen21:07:43

@wildermuthn: nothing will change at all about macros

dnolen21:07:36

Electron, Node.js, hot loading iOS/Android apps, tutorials / demos / online code bins

dnolen21:07:38

stuff like this

wildermuthn21:07:49

How about reader macros?

dnolen21:07:47

Clojure doesn’t support reader macros

wildermuthn21:07:48

I know they’re not generally considered a good idea, but since you’re just evaluating a string, I’d think that it would be possible somehow.

dnolen21:07:06

ClojureScript pretty much doesn't support anything Clojure doesn't

dnolen21:07:49

you can expect this to remain true indefinitely

wildermuthn21:07:25

Ok, makes sense.

meow21:07:58

@gtrak: you were right - taking a callback makes total sense and I like the code a lot better - thanks

meow21:07:06

(defn listen-fps!
  "Repeated callback containing the frames-per-second measured at regular intervals."
  ([callback]
   (listen-fps! callback 500)) ; Measure every half-second
  ([callback interval]
   (let [frame-count (atom 0)
         starting-point (atom nil)]
     (letfn [(measure-fps
              [timestamp]
              (request-animation-frame measure-fps)
              (if-not @starting-point (reset! starting-point timestamp))
              (let [elapsed (- timestamp @starting-point)
                    f-count (swap! frame-count inc)]
                (if (> elapsed interval)
                  (let [fps (->> (/ f-count elapsed) (* 1000) (.round js/Math))]
                    (callback fps)
                    (reset! frame-count 0)
                    (reset! starting-point timestamp)))))]
       (request-animation-frame measure-fps)))))

meow21:07:29

next I'll have to play around with implementations that use channels

gtrak21:07:24

is frame-count going to get arbitrarily large?

gtrak21:07:01

oh wait, I see the reset, nevermind

gtrak21:07:58

I still think you could get rid of the atoms with some more parameter passing

gtrak21:07:25

mimicking loop/recur

meow21:07:42

@gtrak: yeah, I don't have much experience with functional programming

meow21:07:53

so I'm still adapting

gtrak21:07:43

if you work on any large codebase, you'll be surprised how little you actually need atoms

gtrak21:07:46

or any kind of state

gtrak21:07:44

can do a lot of book-keeping with closures and accumulators, eg reduce

meow21:07:24

I'd love to eliminate them from this code

gtrak21:07:34

like an om app has the one atom for all app-state

meow21:07:51

I'm getting used to closures

meow21:07:14

yeah, I've adopted the one-app-state atom approach in my apps

meow21:07:43

this fps code started with code I found in-the-wild that I'm slowly moving into something more elegant

meow21:07:20

by the time its done it will seem so simple and make me wonder why the "in-the-wild" examples were so complex

gtrak21:07:45

lol right

meow21:07:40

I'm already shaking my head in amazement at the examples that got me to this point - boy were they complicated. Just shows that even with clojure one can make a mess.

gtrak21:07:12

i think it's probably easier to make a mess, but it makes the right things more easier than it makes the wrong ones.

eggsyntax23:07:34

@gtrak: "It makes the right things more easier than it makes the wrong ones." I totally propose that as the new Clojure motto. 😉

meow23:07:52

@dnolen: can you clarify and expand on what you mean when you recommend using (goog.object/get …) over aget? Is that in all cases?

dnolen23:07:52

@meow: nothing more to say than what I said. Always use goog.object/get over aget

meow23:07:05

I only use aget in a few places and just changed to goog.object/get but...

meow23:07:27

So will aget be removed from the cljs language?

dnolen23:07:45

aget exists in Clojure

meow23:07:49

Or shouldn't the implementation of aget be changed to use goog?

dnolen23:07:13

This is about using the right thing for the right type

meow23:07:08

Is there no type where aget is appropriate?

dnolen23:07:07

Try aget in Clojure

dnolen23:07:46

If it doesn't work there expect a ClojureScript compiler warning in the future

meow23:07:13

so, its for arrays