<     May 2017     >
Su Mo Tu We Th Fr Sa  
    1  2  3  4  5  6  
 7  8  9 10 11 12 13  
14 15 16 17 18 19 20  
21 22 23 24 25 26 27  
_2_8 29 30 31
00:23 <swart> 5:20 and no breakthrough. I guess I have a long weekend to figure it out
00:24 <pikajude> ouch
04:53 <ec> ljharb: I'm not convinced it's impossible in current JS, either
04:54 <ljharb> i'd love to be convinced
04:54 <ec> since Paws has gotten quiet, I've been seriously considering doing a Flow-style implementation of my dependency-resolution plans
04:54 <ec> directly on top of JS.
04:54 <ec> it'd be beautiful to have something that's partial, strict, and backwards-compatible / falls back to simple package-level versioning of whatever's in npm
04:57 <ljharb> if you provided me a CLI that can report back the semver category of a package, as compared to the prior published version, i would use it in literally every project i touch
04:57 <ec> function minimist@2 (argv, opts) { ... }
04:57 <ec> //=> function minimist/*@2*/ (argv, opts) { ... }
04:57 <ljharb> but all i can think of for how to do is heuristics, which wouldn't be bulletproof.
04:57 <ec> “the semver category of”?
04:57 <ljharb> major/minor/patch
04:57 <ec> no, no interest in heuristic systems, very *very* direct and simple
04:57 <ljharb> right
04:57 <ljharb> ie, you can detect minor pretty easily by just reflecting on the reachable API
04:57 <ljharb> so the hard part is distinguishing minor/patch from major
04:58 <ec> er, maybe we *didn't* talk about this
04:58 <ec> My entire interest is in *surfacing this to humans*
04:58 <ljharb> and the only way i can think of to do that is to somehow get 100% test coverage including type checks, and then run the old version's tests on the new version
04:58 <ljharb> how so?
04:58 <ljharb> (i thought if it requires a human, it's broken)
04:59 <ec> if a human has to decide *if a change is important enough*, it's broken. very different thing — especially when that decision is removed from the code to metadata, like package.json.
04:59 <ec> so okay here's my little rant:
04:59 <ljharb> right, so i agree with your ideal there, just not that it's currently possible :-)
04:59 <ec> gimme a sec and I'll convince you
04:59 <ec> at least, that what I'm talking about is possible; but I'm definitely not talking about what you're thinking
04:59 <ljharb> semantic-release relies on humans deciding on the importance of a change too; it just punts that responsibility to PR authors and reviewers
04:59 <ljharb> ok cool, listening
05:00 <ec> the value of npm, watching JavaScript as a community version and interoperate things, has convinced me that versioning is first-class *the most important thing in a programming language* after the Core Semantic, whatever that may be
05:00 <ec> and the idea that we push it off into extraneous command-line tools, leave it up to third parties who work in the language — ridiculous,
05:00 <ec> but even *including those extraneous solutions* into the original design isn't enough
05:00 <ljharb> i totally agree; if i were writing a new language, making semver be statically determinable would be critical
05:00 <ec> no. no semver.
05:00 <ec> gimme … three and a half minutes, just read
05:00 <ec> typing slow, very tired 🙄
05:00 <ljharb> well, whether it's semver or not is irrelevant, but conveying breakage. lol sure, go for it
05:00 <ec> I'm convinced that versions *have to be surfaced*.
05:00 <ljharb> i'll just watch archer
05:02 <ec> You can't statically-analyze them in entirety. You can't automatically generate them well, without all the trappings of heavily-statically-analyzable languages (strong typing, and more.) And they're important enough, that you can't just *get them ~mostly right~, whenever a human feels like bumping the version, y'know, maybe according to some rules in the
05:02 <ec> README or whatever bro*.
05:02 <ec> you have to literally put them in front of the programmer, as something as-important-as-if-not-more-important-than, the types, names, and control-flow of their code.
05:03 <ec> anyway philosophy aside:
05:03 <ec> every. single. function definition, or exported variable or global. gets a version all its own
05:03 <ec> i.e. minimist@2
05:04 <ec> every. single. callsite¹. gets a version all its own — i.e. `minimist@2(process.argv, { ... })`
05:05 <ec> determining *which package to use* becomes more complicated, but also way more deterministic, and simpler from the user's perspective: whichever ‘package-version’ as a whole satisfies <a given combination> of the consumed endpoints' declared versions; with it being fatal if they conflict (i.e. a completed copy of `foo` was never published with `widget@2`
05:05 <ec> and `quux@3`, because both were bumped at the same time.)
05:06 <ec> this forces the maintainer to report breakage *granularly*, allowing consumers to bee-line for the update information they actually need — but also forces the consumer to stay in version-lockstep to some extent (i.e. they can't start using the features of `quux@3` without reading the changeling for `widget`, and updating their call-site to match)
05:07 <ec> the stuff you're talking about, the crawling, flow analysis (speaking of Flow ... a lot of prior art, here; unsurprisingly, all of it of the area of programming of the most interest to me 🤣),
05:07 <ljharb> ok, so, i'm fine with that conceptually, but that has all the same flaws - it's just semver at function granularity
05:08 <ec> well, what all this is building to,
05:08 <ljharb> and i could *totally* see a babel transform that allowed me to annotate every function with a version, and an eslint rule that required every function to have one
05:08 <ljharb> so you could build that right now
05:08 <ec> is that I believe the core flaw of semver to be that *non-breaking changes should not exist*
05:08 <ljharb> watttt
05:08 <ec> they *exist at all* to basically paste over the lack-of-granularity of semver.
05:08 <ljharb> um, that's not true?
05:08 <ljharb> intentional semantics changes are breaking too
05:08 <ec> *any* change, when looked at granularly-enough, is a breaking change to stuff directly depending on the thing changed.
05:09 <ljharb> hm
05:09 <ec> once you narrow that information down to extreme specificity ...
05:09 <ljharb> ok that's true, but encapsulation changes that
05:09 <ec> then the *endpoints that would stay unchanged in a “patch” release*, stay unchanged entirely
05:09 <* ec> nods
05:09 <ljharb> ie, a function that always returns a string, that starts returning a different string
05:09 <ec> yes yes yes! so stay with me, here,
05:09 <ec> oh wait, what.
05:09 <ljharb> that's a breaking change to something depending on the value, but most things won't be
05:09 <ec> thought you were making a point about sub-dependencies
05:09 <ljharb> also functions that aren't part of the api
05:10 <ec> wait, that's a completely unrelated problem — semver doesn't address that, either
05:10 <ljharb> sure it does
05:10 <ec> there's still a human there deciding “this doesn't feel breaking to most users”, and publishing that change as a patch or w/e
05:10 <ljharb> the breakingness of private code inside my package only affects the semver of the package when it leaks out
05:10 <ljharb> right, so how does your suggestion change that?
05:10 <ljharb> staying with you tho
05:10 <ec> mmmmm it doesn't
05:11 <ec> I don't want to imply this Solves Dependencies — it solves the specific problem I have *with semver*
05:11 <ec> which is that a human has to look at this change, in a granular context, and try to abstract that change upwards, ratcheting through several levels of abstraction in a large project, and (entirely arbitrarily, tbh) decide if it's “breaking enough” to “enough users”
05:11 <ec> so here, the concept of “breaking enough” still exists
05:12 <ec> but every time you ratchet-up, as I mention there, through a layer of abstraction — it becomes softer, foggier. greyed.
05:12 <ec> but w.r.t. *this specific small function*, whether it's “breaking enough” is *very* black and white.
05:13 <ec> changing the string a granular endpoint returns slightly, for instance, is *almost certainly* breaking for that endpoint — and notice, there, I made absolutely no analysis of the importance of that endpoint to outside users, which of them may be using ‘private’ APIs, whether they were depending on an undocumented format or whether that was leaking
05:13 <ec> through.
05:13 <ec> those decisions still exist, y'see, but we're changing where they're made
05:14 <ec> in a way, this is exactly the same *kind* of innovation as versioning itself: taking decisions, re-allocating them and stratifying them; not *solving the root problem directly*, but making it manageable enough to be effectively solved
05:14 <ec> idk anyway enough theory, I'm v. sure I'd have to show you an implementation to have a productive discussion. I was just sure I'd spammed you with this idea way back in the day.
05:15 <ec> ofc so far I've only talked about JavaScript
05:15 <ljharb> ok so i kind of get it
05:15 <ec> (this approach obviously grows **immensely** more powerful in a stricter language, more approachable to analyze statically.)
05:15 <ec> (then you *can* start inferring versions, flowing them through calls, yada yadda.)
05:15 <ljharb> so if you wrote a babel transform and eslint rule combo, and then also wrote a tool that uses the user-reported function-level semver to automatically determine the package's semver level - then you might have something
05:16 <ec> (the JavaScript point being to make this a partially-and-inferred system — giving the user as much value as they're willing to put in additional annotations, just like Flow, but supporting existing npm modules out-of-the-box, just like Flow.)
05:16 <ljharb> in that it still requires a human, but it, as you say, is much smaller and simpler to determine the semverness of a given commit
05:16 <ec> I need some conceptual help working through how to flow versioning *within* a project, though.
05:16 <ec> if you like the idea, I'd love to spam some more words when it isn't quarter-twelve.
05:17 <ljharb> right - you'd also need a tool that enforced that "if i bump function D, did i bump C, and thus B, and thus A?"
05:17 <ec> yessss basically that
05:17 <ec> well, not tooling-level
05:17 <ec> ahhh too many words.
05:17 <ljharb> because with this idea, any change would bump every affected version number across the codebase
05:17 <ec> I'll hit you up next time I'm staring at these screens
05:17 <ljharb> the last tool i mentioned wouldn't be computing semverness
05:17 <ec> but I have Good Ideas there
05:17 <ljharb> it'd solely be "did you make all the necessary version changes"
05:17 <ec> also, although I haaaaate nonbreaking changes, I do recognize their popularity in JS — I'd tentatively have a tuple, basically major.minor
05:17 <ljharb> and if the "version" was "semver", then it'd also match everything else
05:18 <ljharb> if you used normal semver, it'd translate better i think
05:18 <ec> minimist@2.1, incase a *almost certainly nonbreaking* change in a *big-enough API*, that you're starting to run into the same “scope of the version-statement” problem
05:18 <ljharb> you'd need patch too
05:18 <ec> h,
05:18 <ec> hm*
05:18 <ec> convince me
05:18 <ljharb> because a major change in a function might be a patch in a containing function.
05:18 <ec> I deeply hate patch/minor both existing
05:18 <ec> mmmm
05:18 <ljharb> in that it might not alter the containing function's behavior whatsoever.
05:18 <ljharb> so you simply can't avoid patch
05:18 <ec> that ... touches on what I hinted at. okay. so.
05:19 <ljharb> also, `function foo() { return 1 + 2; }` → `function foo() { var a = 1, b = 2; return a + b; }` is a patch-level change.
05:19 <ljharb> ie, refactors.
05:19 <ec> I don't have a *good* solution to this, which is why I want input: my solution takes the stuff I've talked about above (surfacing this to the user, and forcing them to pay attention to it in-a-helpful-and-approachable-way),
05:19 <ec> and makes it waaaaaaay too in-your-face-FUCKING-DEAL-WITH-THIS-RN
05:20 <ec> but: ideally, a point-of-versioning-constraint (callsite.) should be able to express *more complicated* constraints on the version-tree, if necessary
05:20 <ec> i.e. if BigImportantPackageFunction, BIPF(), that you can't really granularly sub-version because the entire package preforms one complicated operation,
05:20 <ljharb> ok yes so: 1) babel transform to strip function-versions; 2) eslint rule to require that every function and require/import and export have an inline version, 3) tool to statically analyze a granular-versioned package and compute an inevitable overarching version number, 4) a tool/eslint rule that enforces that if a granular version changes, then *every
05:20 <ljharb> single thing that depends on that* must change versions too
05:20 <ljharb> give me those 4 things and i think you might have something
05:21 <ljharb> (also, requires of things in package.json wouldn't need a version inline, because you get one already)
05:21 <ec> it may not be enough (as you say) to call BIPF@2(), you want the BIPF@2 that specifically called sub_function@6(), because that version accepted the string you're sending ... or whatever.
05:21 <ec> what you *want* to do with a patch version, there (BIPF@2.2, specifically), if we were to fully-generalize it, would be better expressed as BIPF@2-that-used-sub_function@6.
05:21 <ljharb> sure
05:22 <ljharb> but really you don't want to couple BIPF's version tightly to the encapsulated changes that led to it.
05:22 <ljharb> because my var refactor above should be a private implementation detail.
05:22 <ec> re: package.json-inclusions,
05:22 <ec> I have a whole passel of ideas of how to reduce the line-spam and user-annoyance of this
05:23 <ljharb> well, you could use flow's lead
05:23 <ljharb> and avoid versions that can be inferred
05:23 <ljharb> that'd be tough tho, i think
05:24 <ec> - only allow one version of something per file, and only have to specify the version once in the entire file
05:24 <ec> - allow “include <all the endpoints> of <npm package at npm version>” — basically, chicken-out and fall back on npm-style-semver, even for a package that *is* granularly-versioned
05:24 <ec> - for small verison-numbers, in *my* language work, I was going to use primes (BIPF` == BIPF@2, BIPF`` == BIPF@3) ... template-strings kinda broke that for JS, but we can do something similar
05:25 <ec> well the inferring-failures, we have a leg up on flow: typechecking is useful and applicable more at *development-time*, i.e. “nope. I can't help you with this site.” isn't really a useful and helpful answer ... especially because that means you're *constricting* your code, by committing a specific type instead of allowing the full range of inferred
05:25 <ec> types to be flowed through that point;
05:26 <ec> but version-checking basically only (blockingly) happens at, well, publish-time: and an inference failure means “go add it.”
05:26 <ec> tl;dr I don't really mind adding a bit of relatively-mindless “go add explicit version-numbers where you expected them to be inferred” work to maintainers' publish-time workload, lol, if we're taking away some Serious-Mental-Effort-And-Discussion-work regarding whether something is ~Breaking Enough~
05:27 <ljharb> right, i agree
05:27 <ljharb> if this tool suite we're talking about means i can confidently delegate publishing to travis, then that'd be awesome
05:27 <ec> ugh what's the most JS-friendly syntax/short-form for version-incrementing
05:27 <ec> idk if I'd go that far — you've still got to *interoperate* with JS
05:28 <ec> honestly, this *adds* work for most maintainers. that's the problem with building this on JS, and what's been stopping me.
05:28 <ljharb> that's fine tho
05:28 <ljharb> adding work in exchange for correctness is great
05:28 <ec> if you happen to be in a bubble where lots of your dependencies use this, cool! you save a ton of work
05:28 <ljharb> bugs are much more costly.
05:28 <ec> but if you're building a big thing for Lots Of People, you've *still* gotta make the npm-level decision
05:28 <ec> hmmm
05:28 <ec> hmm.
05:28 <ljharb> if you assume that versions on npm are always correct (hush), then it still helps to just do it in your own package
05:29 <ljharb> and, if your deps are using this, then they'd be more likely to be correct - but you are insulated from that decision, properly.
05:31 <ec> mmmm
05:36 <ec> I come away with the same takeaway: this needs to be made slick-and-useful *within* a package (while focusing, overall, on “making it useful to the ecosystem eventually” as a *direction*) before being published / sold as a Thingie
05:36 <ec> because it really won't be useful within the ecosystem except in niche cases *unless* it's already useful stand-alone — and then you also get “oh this helps my project” network-effect of it growing in the ecosystem
05:36 <ljharb> correct
05:37 <ljharb> so i refer you back to the numbered list above
05:37 <* ec> laughs
05:37 <ljharb> provide that as a suite and we might have a party going
05:37 <ec> well I'm gonna agonize on flow-analysis for a bit more
05:37 <ec> thank god we have *buckets* of tooling on that note nowadays
06:01 <ec> holy jesus, ljharb, are you active in every channel on Freenode
06:01 <ec> how do you ever get any code written — I'm so jelly of your multitasking skill
06:03 <ljharb> haha
06:04 <ljharb> i thrive on multitasking :_p
06:04 <ljharb> :-p
06:04 <ec> feed me
06:04 <ec> https://open.spotify.com/track/0dtOmaY6VBWqe2uXmUgE8H
06:06 <ljharb> no flash, can't see what's there
06:08 <ec> wouldn't matter if you don't have Spotify, anyway
06:08 <ec> High Noon — Feed Me
06:11 <ljharb> i do
06:11 <ljharb> i just only use it on ios
06:43 <ec> « There are many more complications involving subshells, exported functions, "function collapsing" (functions that define or redefine other functions or themselves), traps (and their inheritance), and the way functions interact with stdio. Don't bite the newbie for not understanding all this. Shell functions are totally f***ed. »
06:45 <ec> thx to, uh, glowcoil, I think, for this:
06:45 <ec> http://blog.paralleluniverse.co/2015/08/07/scoped-continuations/
09:24 <ec> well there went half my week
09:24 <ec> https://github.com/ELLIOTTCABLE/minimist-shell/blob/7711a8eb91d5a845f17b70394f8a8511c29c80aa/minimist-shell.js#L2-L264
10:25 mylesborins joined
11:57 _whitelogger joined
12:54 _whitelogger joined
16:15 <joepie91> ec: so after seeing that quote I started reading http://mywiki.wooledge.org/BashWeaknesses
16:15 <joepie91> "Scope: Bash has a simple system of local scope which roughly resembles "dynamic scope" (e.g. Javascript, elisp). Functions see the locals of their callers (like Python's "nonlocal" keyword), but can't access a caller's positional parameters (except through BASH_ARGV if extdebug is enabled). Reusable functions can't be guaranteed free of namespace collisions unless you resort to weird naming rules to make conflicts sufficiently unlikely."
16:15 <joepie91> ehh....
16:16 <joepie91> this does not sound like how scopes work in JS :P
19:21 prophile joined
22:58 krainboltgreene joined
23:59 <yorick> joepie91: you coming to sha2017?