ec changed the topic of #elliottcable to: a 𝕯𝖊𝖓 𝖔𝖋 𝕯𝖊𝖙𝖊𝖗𝖒𝖎𝖓𝖊𝖉 𝕯𝖆𝖒𝖘𝖊𝖑𝖘 slash s͔̞u͕͙p͙͓e̜̺r̼̦i̼̜o̖̬r̙̙ c̝͉ụ̧͘ḷ̡͙ţ͓̀ || #ELLIOTTCABLE is not about ELLIOTTCABLE
<swart> 5:20 and no breakthrough. I guess I have a long weekend to figure it out
<pikajude> ouch
<ec> ljharb: I'm not convinced it's impossible in current JS, either
<ljharb> i'd love to be convinced
<ec> since Paws has gotten quiet, I've been seriously considering doing a Flow-style implementation of my dependency-resolution plans
<ec> directly on top of JS.
<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
<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
<ec> function minimist@2 (argv, opts) { ... }
<ec> //=> function minimist/*@2*/ (argv, opts) { ... }
<ljharb> but all i can think of for how to do is heuristics, which wouldn't be bulletproof.
<ec> “the semver category of”?
<ljharb> major/minor/patch
<ec> no, no interest in heuristic systems, very *very* direct and simple
<ljharb> right
<ljharb> ie, you can detect minor pretty easily by just reflecting on the reachable API
<ljharb> so the hard part is distinguishing minor/patch from major
<ec> er, maybe we *didn't* talk about this
<ec> My entire interest is in *surfacing this to humans*
<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
<ljharb> how so?
<ljharb> (i thought if it requires a human, it's broken)
<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.
<ec> so okay here's my little rant:
<ljharb> right, so i agree with your ideal there, just not that it's currently possible :-)
<ec> gimme a sec and I'll convince you
<ec> at least, that what I'm talking about is possible; but I'm definitely not talking about what you're thinking
<ljharb> semantic-release relies on humans deciding on the importance of a change too; it just punts that responsibility to PR authors and reviewers
<ljharb> ok cool, listening
<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
<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,
<ec> but even *including those extraneous solutions* into the original design isn't enough
<ljharb> i totally agree; if i were writing a new language, making semver be statically determinable would be critical
<ec> no. no semver.
<ec> gimme … three and a half minutes, just read
<ec> typing slow, very tired 🙄
<ljharb> well, whether it's semver or not is irrelevant, but conveying breakage. lol sure, go for it
<ec> I'm convinced that versions *have to be surfaced*.
<ljharb> i'll just watch archer
<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
<ec> README or whatever bro*.
<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.
<ec> anyway philosophy aside:
<ec> every. single. function definition, or exported variable or global. gets a version all its own
<ec> i.e. minimist@2
<ec> every. single. callsite¹. gets a version all its own — i.e. `minimist@2(process.argv, { ... })`
<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`
<ec> and `quux@3`, because both were bumped at the same time.)
<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)
<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 🤣),
<ljharb> ok, so, i'm fine with that conceptually, but that has all the same flaws - it's just semver at function granularity
<ec> well, what all this is building to,
<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
<ljharb> so you could build that right now
<ec> is that I believe the core flaw of semver to be that *non-breaking changes should not exist*
<ljharb> watttt
<ec> they *exist at all* to basically paste over the lack-of-granularity of semver.
<ljharb> um, that's not true?
<ljharb> intentional semantics changes are breaking too
<ec> *any* change, when looked at granularly-enough, is a breaking change to stuff directly depending on the thing changed.
<ljharb> hm
<ec> once you narrow that information down to extreme specificity ...
<ljharb> ok that's true, but encapsulation changes that
<ec> then the *endpoints that would stay unchanged in a “patch” release*, stay unchanged entirely
* ec nods
<ljharb> ie, a function that always returns a string, that starts returning a different string
<ec> yes yes yes! so stay with me, here,
<ec> oh wait, what.
<ljharb> that's a breaking change to something depending on the value, but most things won't be
<ec> thought you were making a point about sub-dependencies
<ljharb> also functions that aren't part of the api
<ec> wait, that's a completely unrelated problem — semver doesn't address that, either
<ljharb> sure it does
<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
<ljharb> the breakingness of private code inside my package only affects the semver of the package when it leaks out
<ljharb> right, so how does your suggestion change that?
<ljharb> staying with you tho
<ec> mmmmm it doesn't
<ec> I don't want to imply this Solves Dependencies — it solves the specific problem I have *with semver*
<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”
<ec> so here, the concept of “breaking enough” still exists
<ec> but every time you ratchet-up, as I mention there, through a layer of abstraction — it becomes softer, foggier. greyed.
<ec> but w.r.t. *this specific small function*, whether it's “breaking enough” is *very* black and white.
<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
<ec> through.
<ec> those decisions still exist, y'see, but we're changing where they're made
<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
<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.
<ec> ofc so far I've only talked about JavaScript
<ljharb> ok so i kind of get it
<ec> (this approach obviously grows **immensely** more powerful in a stricter language, more approachable to analyze statically.)
<ec> (then you *can* start inferring versions, flowing them through calls, yada yadda.)
<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
<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.)
<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
<ec> I need some conceptual help working through how to flow versioning *within* a project, though.
<ec> if you like the idea, I'd love to spam some more words when it isn't quarter-twelve.
<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?"
<ec> yessss basically that
<ec> well, not tooling-level
<ec> ahhh too many words.
<ljharb> because with this idea, any change would bump every affected version number across the codebase
<ec> I'll hit you up next time I'm staring at these screens
<ljharb> the last tool i mentioned wouldn't be computing semverness
<ec> but I have Good Ideas there
<ljharb> it'd solely be "did you make all the necessary version changes"
<ec> also, although I haaaaate nonbreaking changes, I do recognize their popularity in JS — I'd tentatively have a tuple, basically major.minor
<ljharb> and if the "version" was "semver", then it'd also match everything else
<ljharb> if you used normal semver, it'd translate better i think
<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
<ljharb> you'd need patch too
<ec> h,
<ec> hm*
<ec> convince me
<ljharb> because a major change in a function might be a patch in a containing function.
<ec> I deeply hate patch/minor both existing
<ec> mmmm
<ljharb> in that it might not alter the containing function's behavior whatsoever.
<ljharb> so you simply can't avoid patch
<ec> that ... touches on what I hinted at. okay. so.
<ljharb> also, `function foo() { return 1 + 2; }` → `function foo() { var a = 1, b = 2; return a + b; }` is a patch-level change.
<ljharb> ie, refactors.
<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),
<ec> and makes it waaaaaaay too in-your-face-FUCKING-DEAL-WITH-THIS-RN
<ec> but: ideally, a point-of-versioning-constraint (callsite.) should be able to express *more complicated* constraints on the version-tree, if necessary
<ec> i.e. if BigImportantPackageFunction, BIPF(), that you can't really granularly sub-version because the entire package preforms one complicated operation,
<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
<ljharb> single thing that depends on that* must change versions too
<ljharb> give me those 4 things and i think you might have something
<ljharb> (also, requires of things in package.json wouldn't need a version inline, because you get one already)
<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.
<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.
<ljharb> sure
<ljharb> but really you don't want to couple BIPF's version tightly to the encapsulated changes that led to it.
<ljharb> because my var refactor above should be a private implementation detail.
<ec> re: package.json-inclusions,
<ec> I have a whole passel of ideas of how to reduce the line-spam and user-annoyance of this
<ljharb> well, you could use flow's lead
<ljharb> and avoid versions that can be inferred
<ljharb> that'd be tough tho, i think
<ec> - only allow one version of something per file, and only have to specify the version once in the entire file
<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
<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
<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
<ec> types to be flowed through that point;
<ec> but version-checking basically only (blockingly) happens at, well, publish-time: and an inference failure means “go add it.”
<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~
<ljharb> right, i agree
<ljharb> if this tool suite we're talking about means i can confidently delegate publishing to travis, then that'd be awesome
<ec> ugh what's the most JS-friendly syntax/short-form for version-incrementing
<ec> idk if I'd go that far — you've still got to *interoperate* with JS
<ec> honestly, this *adds* work for most maintainers. that's the problem with building this on JS, and what's been stopping me.
<ljharb> that's fine tho
<ljharb> adding work in exchange for correctness is great
<ec> if you happen to be in a bubble where lots of your dependencies use this, cool! you save a ton of work
<ljharb> bugs are much more costly.
<ec> but if you're building a big thing for Lots Of People, you've *still* gotta make the npm-level decision
<ec> hmmm
<ec> hmm.
<ljharb> if you assume that versions on npm are always correct (hush), then it still helps to just do it in your own package
<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.
<ec> mmmm
<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
<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
<ljharb> correct
<ljharb> so i refer you back to the numbered list above
* ec laughs
<ljharb> provide that as a suite and we might have a party going
<ec> well I'm gonna agonize on flow-analysis for a bit more
<ec> thank god we have *buckets* of tooling on that note nowadays
<ec> holy jesus, ljharb, are you active in every channel on Freenode
<ec> how do you ever get any code written — I'm so jelly of your multitasking skill
<ljharb> haha
<ljharb> i thrive on multitasking :_p
<ljharb> :-p
<ec> feed me
<ljharb> no flash, can't see what's there
<ec> wouldn't matter if you don't have Spotify, anyway
<ec> High Noon — Feed Me
<ljharb> i do
<ljharb> i just only use it on ios
<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. »
<ec> thx to, uh, glowcoil, I think, for this:
<ec> well there went half my week
mylesborins has quit [Quit: farewell for now]
mylesborins has joined #elliottcable
_whitelogger has joined #elliottcable
_whitelogger has joined #elliottcable
<joepie91> ec: so after seeing that quote I started reading http://mywiki.wooledge.org/BashWeaknesses
<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."
<joepie91> ehh....
<joepie91> this does not sound like how scopes work in JS :P
prophile has joined #elliottcable
krainboltgreene has quit [Ping timeout: 245 seconds]
jwheare has quit [Ping timeout: 245 seconds]
krainboltgreene has joined #elliottcable
<yorick> joepie91: you coming to sha2017?