Fork me on GitHub
#cljs-dev
<
2017-06-20
>
mfikes00:06:09

I was pondering how we can improve the ^:const description in the “differences” page (https://clojurescript.org/about/differences#_special_forms) for when the next release comes out. It currently has

* `:const` metadata in ClojureScript disallows re-def and supports case. In Clojure `:const` allows re-def but value inlined at use site.
Here is a draft of potential new copy:
* `:const` metadata:
  - disallows re-def in ClojureScript, while re-def is allowed in Clojure
  - supports inlining of static literal values in ClojureScript, while only primitives are inlined in Clojure
  - allows `case` test constants which are symbols resolving to `^:const` Vars to instead represent their values in ClojurScript, while such tests constants are symbols in Clojure 
I found this relevant to Clojure: https://github.com/clojure/clojure/blob/f572a60262852af68cdb561784a517143a5847cf/changes.md#215-const-defs

mfikes00:06:57

Sweet: Test output is now appearing in the CI builds (where it was being swallowed previously): https://travis-ci.org/mfikes/clojurescript/builds/244743749

dnolen14:06:43

@mfikes I don’t think that’s quite right

dnolen14:06:52

Clojure inlining is not limited to primitives

mfikes14:06:20

OK… it can also inline other simple things? (keywords, symbols?)

dnolen14:06:20

also the disallowing redef is only about compiling files - not a REPL thing - so not a big deal for users

dnolen14:06:23

and potentially really confusing to call it out

dnolen14:06:43

@mfikes no you can inline any EDN value you want as far as I can tell

dnolen14:06:24

I think the only thing we should mention is the case will inline ^:const vars

mfikes14:06:26

OK. So perhaps there will be no essential difference between ClojureScript and Clojure with respect to inlining. (That line could be removed)

dnolen14:06:07

the important semantic distinction is that Clojure will eval the const initializer at compile time

bronsa14:06:38

no it wont

bronsa14:06:53

const init is no different than any other var init

dnolen14:06:03

during inlining

bronsa14:06:32

the difference is just intrinsic on the different compilation units

bronsa14:06:48

i.e inlining in clj happens after def evaluation, in cljs happens before eval

dnolen14:06:24

I don’t follow

dnolen14:06:34

if you make a const init expression w/ a side effect

dnolen14:06:42

at every inline site, the compiler will run that side effect

dnolen14:06:52

not in the code

dnolen14:06:59

during inlining at compile time

bronsa14:06:28

yeah, that is not correct

dnolen14:06:34

make an example

dnolen14:06:42

this is what somebody tried when we worked on ^:const

bronsa14:06:43

user=> (def ^:const a (do (println "foo") [1]))
foo
#'user/a
user=> (defn x [] a)
#'user/x

bronsa14:06:04

(def ^:const ..) is not evaluated any differently than (def .. in clojure

dnolen14:06:06

now try this under AOT

mfikes14:06:42

Here is an example that made me think that arbitrary EDN might not be inlined in Clojure:

user=> (def ^:const x [1])
#'user/x
user=> (identical? x x)
true

dnolen14:06:17

@mfikes to me inlining happens if it breaks redef, try it

dnolen14:06:35

(defn foo [] x)

dnolen14:06:46

change x to a new const value, foo will return old

bronsa14:06:19

@dnolen right, under AOT any def is evalauted twice, once during compilation and once during load, again, not specific to const

bronsa14:06:34

it's just that clj will evaluate defs on every ns load, and AOT compiling causes load

dnolen14:06:42

@bronsa ah, k that make sense - clarifies things - thanks

dnolen14:06:08

right so we can’t replicate that

dnolen14:06:13

and that’s what should be called out

bronsa14:06:18

so yeah, there is an evaluation difference between clj and cljs but saying that clj does it at compile time is incorrect

mfikes14:06:19

So, it sounds like the only thing really worth mentioning on the “differences” page is the case behavior.

dnolen14:06:43

@mfikes not quite, @bronsa’s point is correct

dnolen14:06:51

(def ^:const foo (:bar baz))

dnolen15:06:05

in Clojure this will run the initializer and assign it’s value to foo

dnolen15:06:10

then that can be used for inlining

bronsa15:06:54

difference is essentially: clj will inline evaluated values, cljs will just inline unevaluated literals. both require the inlined value to be a valid EDN literal

dnolen15:06:08

so this specific expectation can’t be supported in ClojureScript

dnolen15:06:49

I mean semantically it is supported 🙂 but inlining won’t occur in this case

dnolen15:06:33

(def ^:const foo (:bar baz)) is perfectly valid ClojureScript

dnolen15:06:40

we just can’t inline the result at the compile time

bronsa15:06:56

should that work tho?

dnolen15:06:04

I don’t see why not

bronsa15:06:08

clojure explodes if you try to inline values that can't be inlined

bronsa15:06:23

e.g.

user=> (def ^:const bar (Object.))
#'user/bar
user=> (defn foo [] (bar))
CompilerException java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: java.lang.Object@3d71d552, compiling:(NO_SOURCE_PATH:3:1)

dnolen15:06:48

yeah I’m not concerned about that at all

dnolen15:06:57

too much Clojure internal stuff going on there

dnolen15:06:14

@bronsa one problem is that what we just discussed is surprisingly subtle and easy to be confused about

mfikes15:06:24

* `:const` metadata:
  - supports inlining of static EDN values in ClojureScript, while evaluated values are inlined in Clojure
  - allows `case` test constants which are symbols resolving to `^:const` Vars to be inlined with their values in ClojureScript, while such tests constants are symbols in Clojure 
^ this is my revised proposed language

dnolen15:06:26

and based on what I did for ^:const (an early aggressive and not quite right commit) there’s too much incorrect stuff already in the wild

bronsa15:06:27

it's not easy to talk about in simple terms

bronsa15:06:38

well, easy to understand terms*

dnolen15:06:46

as in people didn’t understand how it was supposed to work and did the wrong thing in existing code

dnolen15:06:57

so forcing Clojure semantics now is just going to break everyone

dnolen15:06:15

“people didn’t understand how it was supposed to work” including me for the last who knows how many years

dnolen15:06:16

@mfikes so now going back I agree

dnolen15:06:23

let’s just mention the case thing

mfikes15:06:44

OK… don’t open the inlining rat hole 🙂

dnolen15:06:40

I think we should say that ^:const will inline compile-time static EDN values and leave it at that

dnolen15:06:44

just so people don’t get tripped up at the REPL

mfikes15:06:56

This simpler language just focuses on ClojureScript:

* `:const` metadata:
  - will cause inlining of compile-time static EDN values
  - causes `case` test constants which are symbols resolving to `^:const` Vars to be inlined with their values 

dnolen15:06:50

sounds good

mfikes15:06:35

I’ll take care of submitting that language when the release is done

favila15:06:32

There's a problem with cljs compiler and transit analysis cache

favila15:06:45

wondering if anyone noticed it or if its a transit version mismatch?

favila15:06:36

Best I can tell from the exception, these write handlers should be one-arg functions, not two-arg method impls: https://github.com/clojure/clojurescript/blob/293b7ddb7c26d525e8fe33c0cf1707cee52c6f8b/src/main/clojure/cljs/analyzer.cljc#L87-L94

favila15:06:34

hah, this is my code

dnolen16:06:33

yeah I would have been surprised if that was the case

dnolen16:06:57

er I mean if this hasn’t been caught before but maybe

favila16:06:13

no it's definitely wrong

favila16:06:23

reader broken too

favila16:06:31

I'll have a (well-tested) fix in a sec

favila16:06:43

but I am really surprised no one ran in to this

favila16:06:50

I guess no one uses transit caching?

favila16:06:35

I guess you only hit it if a js val or pattern gets into the aot

favila16:06:55

(pattern meaning regex)

favila16:06:14

ugh not the aot, the ast

dnolen16:06:30

yeah probably not that common

mfikes16:06:16

For https://dev.clojure.org/jira/browse/CLJS-2110, I’ve posed a question for clarification in #test-check

mfikes18:06:55

@dnolen Summary is it appears there was unintentional breakage. Perhaps that means for ClojureScript is that either it could move to 0.10.0-alpha2 if one is produced in time, or we could revert my change that moved it to 0.10.0-alpha1 if a ClojureScript release becomes imminent.

dnolen18:06:58

@mfikes hrm first thing is we should make a test for it I think 🙂

dnolen18:06:09

and then we can decide if we want to cut a fix for this on Friday

dnolen18:06:19

if you want to submit a regression patch go for it

dnolen19:06:16

@favila, thanks is that :clj only? perhaps that’s why we messed this up - pretty sure sig is different for transit-clj / transit-cljs

favila19:06:41

transit-write-opts and almost everything related to writing analysis files seems to be #?(:clj ...) only

favila19:06:03

all the lines changed by that patch are guarded by :clj