infinisil changed the topic of #nix-lang to: Channel for discussing Nix as a language - https://nixos.org/nix/manual/#chap-writing-nix-expressions - Logs: https://logs.nix.samueldr.com/nix-lang/
ddellacosta has quit [Ping timeout: 256 seconds]
MichaelRaskin has joined #nix-lang
__monty__ has joined #nix-lang
ddellacosta has joined #nix-lang
ddellacosta has quit [Ping timeout: 272 seconds]
ddellacosta has joined #nix-lang
<infinisil> A builtins.tryEval explanation for evanjs
<infinisil> tryEval can only catch `throw`'s and `assert`'s, so this works:
<infinisil> > builtins.tryEval (throw "hi")
<{^_^}> { success = false; value = false; }
<infinisil> > builtins.tryEval (assert false; null)
<{^_^}> { success = false; value = false; }
<infinisil> It can't catch anything else. So e.g. missing attribute, abort, syntax errors. Those all can't be caught:
<infinisil> > builtins.tryEval {}.x
<{^_^}> attribute 'x' missing, at (string):324:18
<infinisil> > builtins.tryEval (abort "nope")
<{^_^}> evaluation aborted with the following error message: 'nope'
<infinisil> Also, note that `tryEval` only evaluates its argument to "the topmost expression" (WHNF if you want to know more)
<infinisil> This means that e.g. it won't recurse into attributes:
<infinisil> > builtins.tryEval { x = throw "nope"; }
<{^_^}> { success = true; value = { x = <CODE>; }; }
<infinisil> However, deepSeq can be used to deeply evaluate a value:
<infinisil> > builtins.tryEval (builtins.deepSeq { x = throw "nope"; } null)
<{^_^}> { success = false; value = false; }
<evanjs> argh. that makes sense. sounds like that might be a cleaner way to do things like this https://github.com/evanjs/nixos_cfg/blob/99679fe183aa5c122834a3f38d753734c27daffa/config/new-modules/vim/vim-hm.nix#L79
<evanjs> ughhh clipboard
WilliButz has quit [Remote host closed the connection]
<infinisil> (repeating in this channel): That vim-hm line can be changed to `config.mine.vim.extraPlugins or []`
<infinisil> Arguably tryEval reasonably only catches throw's, as that's an intentional user-facing error by the programmer, indicating that something went wrong, but the code can handle it
<infinisil> On the other hand, things like missing attributes are the programmers fault because the code couldn't handle all the input it was passed
<infinisil> And the code should be changed to handle that better
<infinisil> Though I'm not sure if there would be a problem if tryEval was changed to catch all errors
<evanjs> yeah that seems to work fine (on one of the nixos machines at least :P)
<evanjs> I'm used to "error handling" in Rust so I'm not unfamiliar with a billion different ways to handle exceptions haha
<infinisil> Hmm, what if there was `builtins.try <expr> <handler>`
<evanjs> ooooo
<infinisil> Or maybe it should be builtins.catch
<evanjs> yeah, I guess when I initially saw tryEval, my mind jumped to e.g. generic try/catch
<infinisil> So e.g. `builtins.catch (throw "hi") (e: e)` returns "hi"
<infinisil> The function in the second argument would get the value that's being throw'n
<evanjs> yeah that sounds pretty straightforward. yeah, map and map_err in Rust (e.g. for Hyper and etc) comes to mind
<infinisil> This would also allow more introspectable errors. E.g. you could `builtins.catch (throw { isSomeError = true; __toString = _: "some error"; }) (e: if e ? isSomeError then "is some error" else toString e)
<infinisil> evanjs: Short explanation of map/map_err for a non-crustacian?
<MichaelRaskin> Then you need introspecting errors to lead to eventual failure, though
<infinisil> Though I know how to read rust types I guess
<infinisil> MichaelRaskin: I'm thinking builtins.catch would also only work for throw's and abort's
<MichaelRaskin> Otherwise which error gets thrown first becomes an interesting evaluation non-determinism
<MichaelRaskin> The problem is: imagine you have two throws on the path to success
<MichaelRaskin> Evaluation order is currently impossible to observe
<infinisil> MichaelRaskin: I don't think that's a problem though. The purity of languages only has to hold for the successful case
<infinisil> Oh
<infinisil> Yeah no I get what you mean
<MichaelRaskin> tryEval _is_ used for cases where the end result is a succesful evaluation!
nbathum has joined #nix-lang
<evanjs> yeah I wasn't sure if that was the best example, just the first thing off the top of my head haha
<infinisil> Yea
<MichaelRaskin> I don't actually remember; I definitely initially implemented tryEval to catch more and it was patched to catch less
<infinisil> Hm yeah so I guess builtins.tryEval is actually pure, but the builtins.catch I proposed wouldn't be
<MichaelRaskin> Returning errors has been discussed since then, I think
<infinisil> If I compare it with Haskell, there I'd use monads to sequence errors
<evanjs> map_ok is basically the same as map_err, just for Ok lol
<infinisil> Lol ok
<infinisil> xD
<infinisil> I see
<evanjs> I'm certain I'm overcomplicating it and this is a generic concept I'm simply forgetting the name of :P
<MichaelRaskin> infinisil: I would expect that in Haskell type-checking would force you to precommit to the order of handling the errors, in a sense
<evanjs> here we go -- https://upsuper.github.io/rust-cheatsheet/ -- Result<T,E> has the signatures for the map functions lol
<evanjs> okay anyway, on a Nix-related note :P -- how about this expression?
<evanjs> pkgs.latest.rustChannels.nightly.rust.override { extensions = [ "miri-preview"]; } (returns: error: attribute 'xz_url' missing, at /nix/store/jazsvnvfwlzd1mry86fgi9jbwp6hqh1p-source/rust-overlay.nix:100:31)
<infinisil> MichaelRaskin: Indeed
<evanjs> tried the lib.or and deepSeq approach but can't seem to get something clean from that
<infinisil> Well yeah you can't catch attribute missing errors within Nix
<infinisil> Will have to fix the code
<evanjs> ahhh okay I didn't realize it was different from e.g. the vim config. bah
<evanjs> I really wish there was some channel for nixpkgs-mozilla haha
<evanjs> or gather feedback, at the very least
<infinisil> Upstreaming sounds good :)
<infinisil> Hmmm
<infinisil> What if Nix had monads
<infinisil> Or maybe rather just some convenient `do` notation
<infinisil> pie_ mentioned to me that he wanted to use builtins.tryEval for writing a Nix testing tool, but that didn't work well because it didn't catch most problems
<evanjs> which is basically where I'm coming from as well. I had no clue it's only wired to catch a few specific errors
<evanjs> I was approaching it as if it was a generic try/catch sort of thing
<infinisil> I guess the problem stands that a proper tryEval would be impure. Because for any sort of useful error reporting you'd need to actually get the error, which as we discovered earlier is impure
<infinisil> So I'm not sure if trying to test Nix from within Nix is a good idea
<infinisil> Maybe you could write a derivation that runs tests though
<evanjs> pffft what, it's not general purpose? :P
<infinisil> Hehe
<infinisil> I've been writing more nixpkgs lib tests recently
<evanjs> and yeah, maybe not, but I definitely think there are some cases that are simple/common enough to merit being handled in a cleaner fashion
<infinisil> And I kind of wish there was a better way to do that
<infinisil> Because currently there's just a bash script that runs `nix-instantiate`
<infinisil> And there's lib/tests/misc.nix which you can `nix-instantiate --eval --strict` to see if it outputs []
<infinisil> If it doesn't, it shows the stuff that failed
<infinisil> (just checks whether two expressions are the same with `==`
<infinisil> So if you want to test proper error messages you need to use the bash script. If not, you can use the misc.nix
<infinisil> But both are about equally painful to write
<MichaelRaskin> infinisil: I guess nowadays one could add a stronger tryEval as a plugin
<pie_> talking to infinisil elsewhere, summary of my pov on tryeval:
<pie_> <pie_> infinisil: iirc i specifically wanted tryeval for writing test suites or something
<pie_> <pie_> theres no (citation needed) reason why it shouldnt work
<pie_> <pie_> like, you can argue for either side (tryeval is warranted/not) but at the end of the day we have tryeval and it sucks :P
<pie_> <pie_> anyway i havent thought through this particularly well, im just approaching this from the POV of my usecase
<pie_> <pie_> where a coding error shouldnt abort the entire tester
<pie_> <pie_> the summary is i think my usecase is valid and from what i saw in the thread that one time the "only" thing preventing it from working properly is someone doing the work
<MichaelRaskin> tryEval seems to be still used in Nixpkgs Hydra jobs, so extending it for pure-debug case … has some drawbacks
<pie_> there is also either a pr or an issue somewhere about making tryeval work for the other 18 out of 20 exceptions that it doesnt work with
* pie_ didnt checks scroll
<MichaelRaskin> For the record: work has been done specifically to nerf tryEval
<MichaelRaskin> First version caught everything
<pie_> maybe have it parametrized like python exception
<pie_> MichaelRaskin: what do you mean by extending it having drawbacks?
<pie_> infinisil: if theres anything useful in the discussion here, might be useful to write it up a bit somewhere
<pie_> otherwise its just lost in irc log
<MichaelRaskin> Well, people now have ideas that some thing are too broken to even complete evaluation on Hydra
<pie_> im not sure what that means but it raises the point about whether the interpreter can even survive a failure:
<pie_> ,?
<MichaelRaskin> REalistically it can
<pie_> theoretically i dont see why it couldnt but idk if in practice its written such
<MichaelRaskin> But Eelco said that it should not
<pie_> so we need interpreter sandboxes :P
<infinisil> So, what exactly are the use cases of a better tryEval?
<pie_> didnt clever do somehting with forking() the interpreter at some point? :P
<MichaelRaskin> Now that sounds like a fragile trick
<pie_> im probably misremembering soemthing completely unrelated
<infinisil> MichaelRaskin: Do you perhaps have a ready link to the tryEval nerfing?
<MichaelRaskin> Let me see…
<pie_> probably not that many references to it in nixpkgs and nix
<infinisil> MichaelRaskin: Oh got it: 1332dd1ed34b45b7e970111da561f20ffe6bc6b2
clever has joined #nix-lang
<MichaelRaskin> It was back in SVN era
<pie_> huh
<infinisil> "tryEval shouldn't catch all exceptions of type Error, since not all of them leave the evaluator in a continuable state. Also, it should be less chatty."
<infinisil> Interesting
<evanjs> so does it currently handle all errors that _do_ leave the evaluator in a continuable state?
<infinisil> Wondering the same
<infinisil> Maybe it could be extended to a bigger set of catchable errors
<pie_> <clever> pie_: it should fork out a child proc for every attribute in that set
<pie_> <pie_> clever: didnt you do something with fork()ing the interpreter at some point? :D
<pie_> <clever> pie_: and then when you try to eval .c.d.e.f, it will as child c, what is at .d.e.f
<pie_> <clever> pie_: see the test-incremental.nix file?
<pie_> <clever> and you can throw child-c into the trash at any time
<pie_> <clever> to recover any ram it was using
<evanjs> my thoughts exactly. at the very least, perhaps it could handle all the non-evaluator-killing errors
WilliButz has joined #nix-lang
<infinisil> I asked eelco to join here. I wonder if he still remembers the reason for that 11 year old commit :P
<MichaelRaskin> All there in the commit message!
<MichaelRaskin> How it applies to the code a few interpreter refactors later is another question
<infinisil> MichaelRaskin: I mean like, how the interpreter is in a non-continuable state
<MichaelRaskin> But apparently he still doesn't want Nix to be a too-big language
<infinisil> MichaelRaskin: I've been wanting to have some "core" specification of Nix
<infinisil> Like a language standard
<MichaelRaskin> Even if the horse has escaped, died, been raised, and then beaten in its undead state
<infinisil> And this would only include the essential bits, getting rid of any weirdness
<MichaelRaskin> The problem is: our cross support is already computation-heavy on the Nix side
<infinisil> MichaelRaskin: Which part of it?
<MichaelRaskin> Ah, maybe rewrite moved a lot of it to shell
<infinisil> Oh yeah but I guess the whole dependency tracking, splicing and stuff
<MichaelRaskin> Well, all this lib.elemAt …
<infinisil> elemAt is cheap fwiw
<MichaelRaskin> Ah right, it's now from builtins
<clever> list ++ [1] is expensive
<clever> map + concatLists will be cheaper then fold and ++
<infinisil> Yee
<MichaelRaskin> But I mean, either elemAt is cheap and the language is going in the general-computation direction, or the other way round
<infinisil> I guess the question is: What needs to be known at eval time and what doesn't?
<MichaelRaskin> Define needs
<infinisil> Because everything that doesn't can really be done by derivation builders
<infinisil> MichaelRaskin: Yeah no idea what that means
<MichaelRaskin> I mean, we are doing _too little_ at evaluation time
<MichaelRaskin> Because extending the unpack logic in stdenv needs a rebuild of everything
<MichaelRaskin> For adding an archive format
<infinisil> Hmm yeah
<infinisil> (arguably that should completely be redesigned)
<infinisil> Like the stdenv
<MichaelRaskin> ()
<MichaelRaskin> ()
<MichaelRaskin> (builderDefs kind of had it in another way
<infinisil> builderDefs?
<MichaelRaskin> I once tried to write a Nix builder generator instead of stdenv monolithic-shell-script
<MichaelRaskin> (which obviously also implied better phase overrides and stuff)
<infinisil> Oh yeah I also started writing something like that at one point
<infinisil> I'd like to develop that further
<MichaelRaskin> Some people seem to really dislike this idea
<MichaelRaskin> Eelco included
<MichaelRaskin> At the same time, people say HPC people need CUDA, so there are now Guix package sets with proprietary packages.
<MichaelRaskin> (not the main one, of course)
* infinisil has to go for now
<infinisil> But very interesting discussions :)
niksnut has joined #nix-lang
__monty__ has quit [Quit: leaving]
noonien has quit [Ping timeout: 272 seconds]
noonien has joined #nix-lang