Fork me on GitHub
#hoplon
<
2015-08-30
>
xificurC19:08:18

@micha: hi! I see that you posted (input :change ...) as an example, didn't you say all of these were removed and one should use on! and do! ?

micha19:08:05

the on! and do! multimethods are for adding your own custom attributes

micha19:08:22

hoplon ships with a number of basic ones, which you can see defined in hoplon.cljs

micha19:08:45

but you can override them with do! or on! multimethod dispatches of your own if you don't like those

micha19:08:08

so once you do for example

xificurC19:08:14

iirc I saw a couple do's implemented but on was dispatching to jquery

micha19:08:18

(defmethod on! :crazy-event
  [elem attribute-key callback]
  (.addEventListener elem "crazy-event" callback))

micha19:08:24

now you can use

micha19:08:50

(button :crazy-event #(.log js/console "the event object is:" %))

micha19:08:13

then whenever "crazy-event" is triggered on that element it'll call the callback

micha19:08:26

yeah on dispatches to jquery by default

micha19:08:46

but like i said that's just the default if no other multimethod dispatch matches

micha19:08:09

if you do (defmethod on! :click [elem key callback] ...) then your omplementation will be used instead

xificurC19:08:53

how does the call know if to look into the on! or do! multimethods? What if you defined :crazy-event on both methods?

micha19:08:21

it dispatches on the type of the value you're setting the attribute to

micha19:08:39

for example, if the value is a function then it knows to call on! because it's a callback

micha19:08:50

(div :something #(...))

micha19:08:05

there's no way that can ever be an attribute, it would make no sense

micha19:08:20

so it's not ambiguous

micha19:08:46

i mean there's no way you intended to set an attribute to that value

micha19:08:09

<div something=???>...</div>

micha19:08:15

the ??? can't be a function

xificurC19:08:51

ah so do only sets attributes. gotcha

xificurC20:08:17

I should really start writing more and ask less simple_smile

micha20:08:20

it's "unidirectional dataflow"

micha20:08:37

the output is events, which correspond to on!

micha20:08:54

and the input generally modifies element properties, via do!

micha20:08:15

the two are completely decoupled

micha20:08:10

it's unidirectional in that do! deals only with setting properties on the element and on! deals only with receiving events fired by the element

micha20:08:15

it's either do!ing something to the element, or calling a callback on! some event fired from the element

micha20:08:34

the on! side of things is blue, the do! side is red

xificurC21:08:07

@micha: that's a quite complicated graph for me simple_smile But thanks!

xificurC21:08:49

so javelin is for the FRP stuff, and castra is the RPC thing. Why use it instead of ajax?

micha21:08:52

it uses ajax for its transport layer

micha21:08:16

but i believe that REST in a web app is always just thinly disguised RPC

micha21:08:28

the REST manifesto has no benefits there whatsoever

micha21:08:56

the only time a GET request makes sense is when you really have a document

micha21:08:11

which is like a static resource, and those are outside the scope of castra

micha21:08:23

you just put those on a CDN and you're done

micha21:08:34

but when you query a database you're doing a side effecting thing

micha21:08:44

so POST is the only sensible method

micha21:08:02

and the path is completely irrelevant with a POST

micha21:08:16

what matters with a POST is the body, which is completely arbitrary

micha21:08:21

and outside the scope of REST

micha21:08:46

so i believe that REST is nonsense when you're communicating with a backend server

micha21:08:10

it's a flawed, leaky abstraction, adding complexity where none is needed

micha21:08:21

all you want to do is call a function on the server

micha21:08:40

with all the transport layer stuff handled automatically

micha21:08:51

i.e. marshalling and unmarshalling, authentication, etc

xificurC21:08:20

funny that rest states rpc as complex and now you say the opposite. I guess there are different opinions on the topic

micha21:08:38

it depends what you mean by "rpc"

micha21:08:48

the castra rpc is a limited form

xificurC21:08:54

I was asking why all castra requests are POST in a previous post, so I guess you still remembered that simple_smile

micha21:08:23

the problem with RPC is when you want to have rpc stubs in the client that are indistinguishable from regular functions

micha21:08:42

like you want to not have to know if a function is executing on a remote host or not

xificurC21:08:42

I asked because I remember someone telling me in short that GET is for when you make no side effects on the server. In the case a query would be a GET, which castra surely does

micha21:08:49

castra doesn't provide that

micha21:08:18

there is no such thing as "no side effects" when talking to the server in a real application

micha21:08:26

there is authentication state, things like that

micha21:08:36

they're stateful calls, not stateless as GET is supposed to be

micha21:08:55

also you're querying over a database that is constantly changing

xificurC21:08:59

the person's words actually where 'when you fire the GET method n times and you always get the same result'

micha21:08:08

so there is no parallel with a document with a last modified time

micha21:08:25

yeah thats just not the case for ajax requests in the real world

micha21:08:37

if you have an immutable thing you just write it to a file and serve it from a cdn, right?

micha21:08:45

you don't serve that from your database, that's insane

micha21:08:09

the database is where you have constantly changing state

micha21:08:27

so GET is never really appropriate there, or very useful

xificurC21:08:26

I understand, thanks! I have another question but I have to ask another question before asking the first question - do you mind all these questions?

xificurC21:08:44

or me saying question so many times simple_smile

xificurC21:08:06

I ask a lot

micha21:08:51

haha no worries!

micha21:08:56

fire away

xificurC22:08:48

This is still all very theoretical, but a thought you could clear for me - in the om talk David said it's good to have all state in 1 var. Before hearing him I actually thought of it as a limitation. I understand it can be useful to have all state in 1 place so that it is easy to dump it as a whole. But it seems to have drawbacks, like how do you "listen" to changes deep in the state. Which is probably what cursors were invented for, right. Still the whole thing seems to me upside-down. Wouldn't it be nicer to keep state in small chunks that are a) easy to understand, b) easy to listen on? And you can always just write something that composes them together into 1 var. Does this make any sense?

micha22:08:36

the big problem with having everything in 1 atom is that everyting in there needs to be named

micha22:08:53

it's basically serving the same purpose as the namespace system we already have

micha22:08:06

but it's worse because there is no way to use scope in there

micha22:08:18

which is a key thing in building lisp programs

micha22:08:29

so i don't agree that having all things in one atom is good at all

micha22:08:42

with hoplon we have as many atoms as we need

micha22:08:08

and we can connect them together for querying over with formula cells

micha22:08:39

state can be contained in anonymous cells, which is vitally important

micha22:08:54

when everything needs to be explicitly named you can't form abstractions

micha22:08:07

because abstraction by definition is hiding internal structure

xificurC22:08:16

yeah, seems more logical to me than dumping everything in 1 var. I mean that's why we have vars and scope etc

micha22:08:16

which you can't do when you have a global atom where nothing is hidden

micha22:08:27

Lisp Can Do It

micha22:08:39

if you find yourself reimplementing lisp in your program you're probably doing it wrong

xificurC22:08:40

who wants to code in a programming language where you can only define 1 global variable

micha22:08:12

the reason why they like it is because it gives a more or less atomic update kind of semantic

micha22:08:21

like when you swap the one atom it changes atomically

micha22:08:26

so that's useful and good

micha22:08:51

but javelin cells also accomplish that, without requiring a single atom contains all state

xificurC22:08:38

on a side-note, I just clocd hoplon, javelin, castra and cljson, wth? It's like 4k loc altogether

xificurC22:08:59

that's like java hello world size

micha22:08:21

haha i know

micha22:08:25

clojure is amazing

micha22:08:56

i'm working on this web application in hoplon at work

micha22:08:17

basically a rewrite of a c# mvc app

xificurC22:08:28

seriously, with that loc I don't think you are allowed to call it a framework ­čśŤ

micha22:08:48

the entire project in hoplon is about the same amount of lines of code as a single spark template file in the c# app

xificurC22:08:30

cool simple_smile How does it compare speed-wise?

micha22:08:06

hoplon app is way faster

micha22:08:26

because it's a lot simpler code, so we don't do as much unnecessary database stuff

micha22:08:50

the c# stack with the MVC and activerecord type ORM is really hard to work with

xificurC22:08:02

how does cljs compare to js btw? I didn't see any comparison on that

micha22:08:07

so you end up doing all kinds of unnecessary or suboptimal things just to get somehting that isn't filled with bugs

micha22:08:17

well i make business applications

micha22:08:31

people load the app and work with it for a long time

micha22:08:38

think gmail

micha22:08:01

so what's important is features, reliability, and speed once you're already loaded

micha22:08:06

like gmail

xificurC22:08:22

like java you mean simple_smile

micha22:08:23

if the page takes 900ms to load or whatever nobody cares

micha22:08:33

because you only do it once

micha22:08:41

like this slack app

micha22:08:52

nobody cares if it takes 5s to load slack

micha22:08:59

because you just keep it open

micha22:08:23

i think all business applications are like this

micha22:08:38

so i don't spend much time obsessing about perf benchmarks or anything

micha22:08:57

they're meaningless to me

micha22:08:57

what i care about is developer productivity

xificurC22:08:01

wait slack isnt cljs no?

xificurC22:08:14

that was just a general example right

micha22:08:19

no, i'm just saying

micha22:08:00

when you have 4k LOC total you can add features and maintain your application way more efficiently

xificurC22:08:02

I think a lot about productivity when I have to write these VBA monstorsities at my work simple_smile

micha22:08:07

which is where my bread and butter really is

xificurC22:08:10

s/think/dream/

xificurC22:08:28

reminds me of @alandipert 's comment from your clojurewest talk about the pile of garbage that circles around in the oceans

xificurC22:08:45

VBA is the dark side of the undead technology

micha22:08:45

my priorities are:

micha22:08:49

first make it possible

micha22:08:54

then make it elegant

micha22:08:57

then make it fast

micha22:08:16

you can't really do those in any other order

xificurC22:08:19

so it's cljs -> css -> boredom

micha22:08:21

or you end up with spaghetti

xificurC22:08:54

oh you meant code, not the app

micha22:08:06

how do you mean?

micha22:08:28

i mean it's way easier to optimize an elegant thing that works correctly already

micha22:08:42

than it is to make something fast and crazy into something elegant

xificurC22:08:30

> premature optimization is the root of all evil right

micha22:08:36

like i want my application to perform well, but it only needs to perform well enough

micha22:08:49

that still needs to be balanced against other equally important concerns

micha22:08:58

like features, maintenance costs, etc

alandipert22:08:18

i'll think a little more and be on later

alandipert22:08:35

i'm not a huge fan of .core, i think if we're going to have something redundant in a package name it should be meaningful

alandipert22:08:44

the way a hoplon segment would be

alandipert22:08:58

e.g. (ns hoplon.javelin) vs (ns javelin.core)

alandipert22:08:00

i suppose i would advocate:

hoplon/hoplon -> hoplon(/hoplon) -> hoplon.hlisp?
hoplon/javelin -> hoplon(/javelin) -> hoplon.javelin
hoplon/castra -> hoplon(/castra) -> hoplon.castra

onetom22:08:47

the last one is namespace

micha22:08:52

@alandipert: consider the redundancy question in this light:

onetom22:08:54

so it should contain dots

micha22:08:09

hoplon.core
hoplon.test
hoplon.svg

onetom22:08:11

im not a fan of .core either

onetom22:08:32

BUT it allows more symmetry again...

micha22:08:58

you can't have single segment namespaces in clj or cljs currently

micha22:08:12

so just hoplon or javelin isn't really an option

xificurC22:08:14

@micha: thanks for the chat! See you later guys

alandipert22:08:44

hoplon.good-word-for-things-that-interface-with-browser

onetom22:08:55

@xifi: you had great questions. my team will benefit from it too!

onetom22:08:15

alandipert: hmmm core? simple_smile

micha22:08:39

core is the known workaround name

onetom22:08:50

hoplon.html

micha22:08:58

but it also has other things in there

micha22:08:10

it's mostly hoplon.dom

onetom22:08:12

it sounds like a heresy though simple_smile

micha22:08:22

hoplon.hlisp

onetom22:08:47

yeah, and then it wont be a surprise if u alias it to h

micha22:08:52

but it's not really such a focused namespace

micha22:08:15

it has other stuff in there, which we could extract into other namespaces i guess

onetom22:08:18

but then again, it's not something which a beginner can easily guess or keep it mind...

micha22:08:47

if you're using the hoplon boot task you don't need to know about those namespaces really

micha22:08:50

when you're starting out

onetom22:08:56

alandipert: here is a challenge which might cast a better light on the question: would you rename javelin.core to hoplon.javelin?

onetom22:08:23

is javelin a sub-component of hoplon or is it a general purpose library on its own?

onetom22:08:48

what does a github org group together? what does an artefact group id group together?

onetom23:08:28

micha: what's the usual problem if i get this javelin.core is not found?

java.util.concurrent.ExecutionException: clojure.lang.ExceptionInfo: failed compiling file:/Users/onetom/.boot/cache/tmp/Users/onetom/p/hoplon-mini-example/n7y/yuvvjw/boot/cljs/main969.cljs {:file #object[java.io.File 0x4bf0aa08 "/Users/onetom/.boot/cache/tmp/Users/onetom/p/hoplon-mini-example/n7y/yuvvjw/boot/cljs/main969.cljs"]}

             clojure.lang.ExceptionInfo: failed compiling file:/Users/onetom/.boot/cache/tmp/Users/onetom/p/hoplon-mini-example/n7y/yuvvjw/boot/cljs/main969.cljs

    data: {#object[clojure.lang.Keyword 0x4b6d72ef ":file"] #object[java.io.File 0x4bf0aa08 ".../main969.cljs"]}

             clojure.lang.ExceptionInfo: Could not locate javelin/core__init.class or javelin/core.clj on classpath.

    data: {#object[clojure.lang.Keyword 0x4e7f763e ":tag"] #object[clojure.lang.Keyword 0x4d29351 ":cljs/analysis-error"]}
          java.io.FileNotFoundException: Could not locate javelin/core__init.class or javelin/core.clj on classpath.
this is the file it's complaining about:
(ns boot.cljs.main969 (:require tailrecursion.hoplon.app-pages._index_DOT_html))

(do)

alandipert23:08:08

@onetom: i would, because hoplon is the name of the effort and group of people aware of that effort who have the most interest in javelin

alandipert23:08:45

i suppose the hlisp part is the 1 of 3 parts that isn't useful outside of a "hoplon" app

alandipert23:08:53

since it depends on javelin

alandipert23:08:14

i guess castra does too

alandipert23:08:26

so it would seem that if anything, hoplon is defined more by javelin than anything else lol

alandipert23:08:36

maybe we should switch the names simple_smile

onetom23:08:15

someone actually have it but no content on it

onetom23:08:16

anyway, this compilation error is super annoying and i have no idea what causes it. this same code works in an other project:

(ns business.logic
  (:require-macros
    [javelin.core :refer [defc defc=]]))

(defc person "Bobak")
(defc= greeting (str "Hello " person))

onetom23:08:15

it stopped working even with

[tailrecursion/boot-hoplon "0.1.3"]
[tailrecursion/hoplon "6.0.0-alpha7"]