Fork me on GitHub
#hoplon
<
2016-12-09
>
flyboarder03:12:48

@alandipert: is it weird to call a function before a let?

flyboarder03:12:46

Sorry Alan just a random lisp question for you

flyboarder03:12:03

Ah nvm cljs has delay, I can use that instead

alandipert03:12:10

@flyboarder curious, have an example?

alandipert04:12:49

speaking of lisp, this is the best-looking guide i've run across if anyone is interested in trying out a lisp machine http://www.loomcom.com/genera/genera-install.html

alandipert04:12:17

includes direct links to world images of dubious legality

alandipert04:12:29

looks like a doto joint

alandipert04:12:11

eg

(let [userService (doto (.service app "/users")
                                          (.before (.-hooks before))
                                          (.after (.-hooks after)))] ...)

flyboarder04:12:32

@alandipert so I had something similar but do I call the app.use ahead of that let? it looked funny in a defn

alandipert04:12:52

i see what you're saying

flyboarder04:12:55

What is different about a doto and just threading the object?

alandipert04:12:21

doto returns the "topic", not the value of the last expr

alandipert04:12:26

ie the code returns (.service app "/users") above

alandipert04:12:43

err, the object produced by (.service app "/users") that is

flyboarder04:12:50

wow learned a thing!

alandipert04:12:57

with-let in javelin etc is a slightly more general doto-ish thing

alandipert04:12:09

for situations where yuo need to return an object, but also mutate it before returning it

flyboarder04:12:33

right instead of whatever the last function returns

flyboarder04:12:28

@alandipert with using doto I can just drop that part in the let body tho since the hooks are part of the line after

alandipert04:12:57

lookin sharp

alandipert04:12:05

i don't think you need the let tho, defn body is implicit let

flyboarder04:12:19

yeah im gonna use this version i think:

flyboarder04:12:49

so I can keep chaining the api fn

alandipert04:12:01

oh, perfect with-let situation then

alandipert04:12:10

(defn api [app path svc & [{:keys [before after]}]]
  (with-let [app (.use app path svc)]
    (doto (.service app path) (.before before) (.after after))))

alandipert04:12:14

rhombus-preserving

alandipert04:12:30

lol it's just an aesthetic micha develpoed

alandipert04:12:45

the idea that sexps should flow to the bottom right, like rhomboid shape

flyboarder04:12:11

oh I see, lol makes sense, looks better that way

alandipert04:12:30

The Perfect Defn Shape

alandipert05:12:36

speaking of rhomboids, as-> is a neat thing to be aware of

alandipert05:12:51

(with-timeout 0
  (as-> (new-machine) |
    (select-keys | [:stack :line :inst-ptr])
    (merge @machine |)
    (tramp |)))

dm311:12:09

shudders

dm311:12:26

you have to use (as-> x $ ..) as a placeholder 🙂

micha12:12:41

haha @alandipert invented with-let, not me

micha12:12:02

i just appreciate the rhombus in all of its glory

candera15:12:11

Finally leveled up to use lenses.

micha15:12:17

are they good?

candera16:12:17

Yes, in terms of making the code better. But thing is, I think I’m seeing an issue where they don’t work right under a dosync. I have something almost exactly like the path lens in the example, only when I do two swaps in a dosync against the lens, the second one updates the original value, not the value following the prior swap. I was able to fix it by writing a single swap, but it broke my expectations about change visibility inside transactions.

micha16:12:23

interesting, that seems like a bug

micha16:12:58

you're updating another lens in the transaction?

micha16:12:24

lenses are not exactly input cells

micha16:12:29

so their values won't update inside the transaction

micha16:12:38

but maybe that's wrong

candera16:12:20

I’m updating the same lens twice. (dosync (swap! l assoc-in [:x :y] :new-y ) (swap! l assoc-in [:x :z] :new-z)) and I wind up with {:x {:y :old-y :z :new-z}}

candera16:12:18

Because - AFAICT - the second swap gets the version of the lens that existed at the beginning of the transaction. IMO, this is incorrect. Transactions should isolate changes made by other changes, but changes made by this transaction should be visible immediately. This is how clojure.core/dosync works, as well as how transactions in SQL databases.

micha16:12:52

yeah the contract with javelin is slightly different

micha16:12:08

inside a transaction only updates to input cells are visible inside the transaction

micha16:12:19

because the transaction defers updating formulas

micha16:12:42

since formulas can be stateful it's important that a formula does not update more than once per transaction

micha16:12:11

the fact that you can swap! or reset! on a lens is a weird case

candera16:12:50

Perhaps, since the contract is different, overloading swap! and reset! was a mistake.

candera16:12:57

E.g. lswap! lreset!

candera16:12:22

But per Rich’s conj talk, please don’t change swap! and reset!. 🙂

candera16:12:15

Anyway, I’m just coming to this, so there’s probably loads I’m missing. I’m happy to understand why it’s doing what it’s doing, and to be able to do what I need to do.

micha16:12:33

i guess this is similar to clojure transactions where there is the clojure.core/io! thing

micha16:12:00

because statefulness in STM transactions has the same issues as what we have with cells

micha16:12:24

perhaps what we want is a similar form in javelin transactions?

candera16:12:31

Well, it’s already a little weird to be in analogy territory, because clojure.core/swap! != javelin.core/swap!

candera16:12:44

javelin’s swap! is more like alter! or swap! depending.

micha16:12:49

there is no javelin.core/swap! 🙂

micha16:12:01

we just implement the ISwap protocol

candera16:12:25

With extra semantics, though, yes?

candera16:12:43

And is ISwap documented?

micha16:12:16

it's a cljs thing

candera16:12:46

Right, but just because it’s in the source code doesn’t make it part of the API. It might be - I’m a CLJS dunce.

candera16:12:14

Sorry - not trying to drag you down a “you should have done it different” rathole.

micha16:12:21

no, not at all

candera16:12:25

But happy to have the discussion if it’s interesting.

micha16:12:29

it's hard to design apis

candera16:12:44

So, one question I have is, what is the value in using swap! instead of your own function?

candera16:12:18

Deref and Meta seem legit.

candera16:12:30

But swap! doens’t talk about transactions, and in this case it seems to get tied up in that.

micha16:12:48

the case with input cells is i think analogous

micha16:12:07

i mean when you call swap! on an input cell it's acting the same way as an atom would

candera16:12:15

No, it’s not, because of transactions.

micha16:12:17

it just happens to have watches on it that do weird stuff

candera16:12:24

Ah, I see the perspective there.

micha16:12:38

yeah with input cells they do get swapped immediately

micha16:12:50

it's just the watches that are delayed

micha16:12:06

lenses though have a different semantic somewhat

micha16:12:25

like when you do (swap! a-lens inc)

micha16:12:40

you are not guaranteed to have changed the value in a-lens

micha16:12:55

you are just commanding a change that may or may not be honored

micha16:12:01

or the value may change to some other value

micha16:12:18

and that's kind of why it can't do it inside the transaction, too

candera16:12:20

Gotcha. It’s about the watches.

micha16:12:36

right yeah, the value in the lens is computed in the watches

candera16:12:40

cells themselves really are atoms, in a sense: no guarantees about coordination baked into them.

micha16:12:50

input cells, right

micha16:12:58

formula cells you can't swap or reset

micha16:12:10

and those are the ones that guarantee dependency relationships

micha16:12:26

then lenses appear and are weird

candera16:12:35

Well, are they?

micha16:12:14

there is no guarantee with a lens that (< @a-lens (swap! a-lens inc)) is true

candera16:12:15

Hmm, I sort of see that they are.

micha16:12:49

so if you accept that then it seems ok if they don't update inside a transaction

candera16:12:28

That guarantee is present for an input cell, yes?

micha16:12:33

right, totally

candera16:12:43

Huh, so that’s where having swap! work on both of them seems weird to me.

micha16:12:56

yeah that's an interesting point

candera16:12:05

It’s almost like agents are a better analogy for lenses.

candera16:12:17

And of course in clojure you have a separate API for mutating an agent vs an atom.

candera16:12:26

lswap! 😉

micha16:12:16

i wonder if it would make sense to queue operations on lenses

micha16:12:20

inside a transaction

candera16:12:42

Well, you sort of are, if you’re holding formula propagation.

micha16:12:57

right but in your case though

micha16:12:03

two calls to swap

micha16:12:16

if we queue them your case would work as expected

candera16:12:22

If you preserve order, yes.

candera16:12:30

Making the analogy to agents even stronger.

micha16:12:53

i've never used agents, i must admit

candera16:12:58

I might suggest a new datatype rather than changing anything about lenses, if you do explore down that road.

candera16:12:09

Yeah, I think I maybe used agents once.

candera16:12:26

But they compose very nicely with refs.

micha16:12:47

the big difference i see between javelin and clojure stm etc is the dependency graph

candera16:12:03

Sure, it’s an analogy. Definitely need to tease out the places where it breaks.

micha16:12:14

the "push" architecture is the essential difference maybe

candera16:12:21

Interesting to think of formula cells as CSPs, though.

micha16:12:34

like it's very rare that you have a program that is "resident" in that way on the server

micha16:12:43

where you have cells that last over multiple transactions

candera16:12:02

Huh? My cells in my programs stick around for a long time.

micha16:12:17

like on the server you want to tear everything down after a transaction is processed

micha16:12:31

so you don't have tons of cells in memory updating themselves

micha16:12:52

you'd use a pull type of architecture there with functions talking to a database or something like that

candera16:12:53

I don’t find that relevant. I have an SPA and I want my cells to stick around. cell lifetime seems to be orthogonal.

candera16:12:18

Ah, but you’re talking about a CSP metaphor, right?

micha16:12:18

right, i was just saying that there are not any things in clojure that are like that

micha16:12:44

like in clojure you don't generally want to have things like formulas that are updating even when nothing is looking at them

micha16:12:58

because you're going to be running on a highly loaded server probably

candera16:12:07

Although those are processes, arguably not identities.

micha16:12:15

and they're like pipelines

candera16:12:48

Well, they’re processes. Not necessarily pipelines. Graphs of processes, more generally.

candera16:12:05

Directed, potentially cyclic.

candera16:12:36

I’d also challenge the “probably running on a highly loaded server”. That’s far from universal.

candera16:12:43

Anyway, I need to get a drink and go back to hacking. I will noodle on this a bit - definitely more educated about cells and lenses than I was half an hour ago - thanks for that!

micha16:12:51

haha any time

alandipert16:12:32

the queueing semantic

micha16:12:33

the difference between cells and channels though is interesting

micha16:12:51

i have not found any application for cells in a server really

alandipert16:12:55

i ran across a thought experiment kind of like this, thinking about add-watch order

dm317:12:39

in my experience backend is all about the queue management, while a cell has two choices: an infinite queue or no queue

micha17:12:27

yeah the backend needs to decouple processes from each other in time

micha17:12:42

queues and whatnot

micha17:12:50

cells kind of do the opposite

micha17:12:04

like asynchronous cells seems like it would be useless

micha17:12:17

like if you don't have the guarantee that formulas are correct

dm317:12:48

well, you can imagine long-running state-machines, kind of like the actor model

dm317:12:55

which is yet another angle

micha17:12:36

yeah i could imagine also using cells to order the path of data through queues, possibly

dm317:12:43

but if you take the Javelin cells verbatim - it's pretty useless

micha17:12:06

like javelin propagates a change by ordering a sequence of thunks

micha17:12:16

and it executes them all synchronously

micha17:12:46

maybe the async propagation would establish an ordering of queues

micha17:12:36

i guess you'd end up with synchronous thing then at the end

micha17:12:47

hung off of a queue

micha17:12:57

i really can't think of how cells could work asynchronously

dm317:12:27

nope, it must be something with a buffer in front

dm317:12:34

like CSP channels or actors

micha17:12:59

maybe for some kind of embedded system or something like that

micha17:12:26

like a PLC kind of

alandipert17:12:32

seems like they are systems at different granularities and so aren't really comparable

micha17:12:45

the PLCs sort of work that way now

micha17:12:50

they're like one formula

micha17:12:09

you update the inputs and it updates the outputs

alandipert17:12:16

like in x86 asm when you assign a register, it's async in that your instruction returns before anyone can check if the register has changed

micha17:12:56

presumaby though another instruction won't run until that register has been changed though, right?

alandipert17:12:03

we don't know

alandipert17:12:14

if you want to know you need to program in a certain way

micha17:12:22

i don't follow

alandipert17:12:01

like the essential thing is order, ordering writes in particular

alandipert17:12:15

cells and csp share this

alandipert17:12:24

but then diverge

alandipert17:12:49

they are clearly both general, since you can implement a cellsish thing on csp and vice versa

micha17:12:07

like if i put a value in a register and then call an instruction that uses the value in that register, there is a guarantee that the move instruction has moved the value before the next instruction uses that register, no?

alandipert17:12:11

i guess i was thinking no because of interrupts and threads

alandipert17:12:19

ie the register is volatile

alandipert17:12:39

hm a bad example lol

micha17:12:59

i wonder like

micha17:12:07

in propagate! for example

micha17:12:33

what if it connected queues to things in a certain order

alandipert17:12:10

yeah, i think there might be some value in stable ordering

alandipert17:12:20

right now its undefined because watch action order is

micha17:12:32

you could then guarantee that the values would flwo through the queues in a certain order

micha17:12:57

and the computations would then happen in a certain order too because they're triggered by things coming to them via a queue

alandipert17:12:09

when i was looking at the for-tpl weirdness

alandipert17:12:28

at one poitn i thought what i needed was for some code to run both after the data cell had changed, and after for-tpl did its business, whatever that was

micha17:12:09

you could do that though

micha17:12:13

with setTimeout

micha17:12:24

in a watch on the seq

alandipert17:12:25

yeah, that's what i edned up doing

micha17:12:31

why did you need that?

micha17:12:53

i guess to flash some thing that just indicates that a change happened?

alandipert17:12:05

i wanted to set scrollTo on the textarea parent of the for-tpl

alandipert17:12:10

only after its contents had possibly changed

micha17:12:28

oh interesting

alandipert17:12:55

i can't remember who, but like a year ago, it hink martin klepsh

micha17:12:55

yeah that makes sense

alandipert17:12:04

ran into something and proposed that -tpl shouldl define a lifecycle i think

alandipert17:12:19

and i thought about it again the other day, and i think like, (loop-tpl :after (fn [] .. would be bad

micha17:12:32

yeah the setTimeout seems perfect

alandipert17:12:35

but it's unnecessary if we just assure you -tpl and watches are run in order of establishment

alandipert17:12:53

i didn't like it because it wasn't transitive

alandipert17:12:03

but maybe that's not bad

micha17:12:09

transitive in what way?

alandipert17:12:24

in the sense that if in the add-watch function i reset! another cell, the order is undefined again

alandipert17:12:48

but maybe that's ok since i've left the model

micha17:12:11

also i don't think there is any way to do it "right"

micha17:12:21

like which should be the order?

alandipert17:12:29

order of watch establishment

micha17:12:31

i could imagine wanting inverted order sometimes

micha17:12:47

because maybe the -tpl thing is in another component

micha17:12:53

and the user doesn't have direct access to it

micha17:12:59

eg. the component is adding watches

micha17:12:10

and the user wants to add a watch that goes before or after that one

alandipert17:12:14

definitely leads to lifecycle hell

alandipert17:12:21

ie i want it to run before this watch bu after that one

micha17:12:28

immediately need IBeforeAfterWatch

micha17:12:47

that's what cells save you from

micha17:12:53

so it should be some kind of formula

alandipert17:12:53

ok, im on board with the timeout workaround

alandipert17:12:10

it would have done it in that case and i opted out of being saved by cells

micha17:12:30

i meant in the case where you want to do this then before that thing but after the other thing

micha17:12:53

that kind of situation seems perfect for cells to handle

alandipert17:12:33

im just thinking, thats the road that adding a watch order semantic leads too

alandipert17:12:39

better not to go down it

micha17:12:45

yeah totally agree there

raywillig17:12:36

not to mention that a watch can be misconstrued sartorially

micha17:12:48

i think that "promoting" timeouts to cells when it starts to get complicated actually has a beneficial effect

alandipert17:12:09

@raywillig looked it up, giggled

micha17:12:12

like you end up lifting these things out of the component

alandipert17:12:40

the key thing this was to effect a dom node

micha17:12:40

like instead of handling the scrolling inside the component that's managing the list of elements

alandipert17:12:45

i was not resetting anythin in there

micha17:12:20

like the reason why you need the lifecycles is when you want to encapsulate the scrolling or whatever inside the component

micha17:12:32

and you also want to expose it in some way too

micha17:12:44

like so the user can add their own baggage to the scrolling operation

alandipert17:12:07

maybe it should be a prop-cell situation even

micha17:12:18

seems like if you have to lift that to cells you'd end up passing something in to the component that does the scrolling

alandipert17:12:21

make the height of the thing continuous and then put the scrolling in a formula

micha17:12:23

instead of having it built-in

micha17:12:36

that is good because at that point you need to separate concerns anyway

micha17:12:51

if you need the watch ordering it's because you need to factor

micha17:12:07

pass that in as a separate unit

alandipert17:12:16

like when i add a thing to the lines cell, a watch puts the height of the containing textarea in a cell, which is watched by a thing that scrolls to new vals

micha17:12:34

i mean in the case where you have a component that does that

micha17:12:39

but also the user needs to do other things

micha17:12:46

some before and some after the scrolling

micha17:12:48

and so on

micha17:12:06

seems like if you get to that place, you have identified some factoring that needs to be done

micha17:12:31

factor that behavior out into a thing that you pass in to the component now

micha17:12:45

and the component then knows nothing about all of the scrolling anymore

micha17:12:22

rather than make a complex interface to the component that basically proxies watches

micha17:12:30

i guess you are firing events there

fiddlerwoaroof23:12:52

Should (cell= (print :foo cell)) always log to the console?

fiddlerwoaroof23:12:33

For some reason, it isn't logging on update, at least when cell is a lens

micha23:12:28

@fiddlerwoaroof it should print every update

fiddlerwoaroof23:12:36

Hmm, I'll have to experiment with this, it's been pretty inconsistent