Just foxing about.
Okay, it turns out that 1ML might be exactly what we want. Good introductory talk: https://www.youtube.com/watch?v=42Wn-mXWcms
I'm sure it will find all sorts of exciting things, even if only with the parser.
quickcheckseems to be the go-to solution, and there's a small useful intro to using it for parsing here: https://adriann.github.io/rust_parser.html
Well I tried updating to tarpaulin 0.18.0-alpha2 on my local machine and it made most of the
rustcICE's at least once, and the error output is 6 megabytes. I'm honestly pretty impressed!
It appears to build
endtoend.rssuccessfully, in fact, and somehow doesn't execute it with the right command line args. Running the resulting test binary by hand is what makes all that stuff happen.
rustcand I don't think anything in the generated test binary itself does, so Something Weird is happening somewhere.
With the advent of
garnetcas a separate process,
tarpaulina) takes about a minute or two to start executing, and thus times out builds.sr.ht, and b) doesn't check for code executed in the resulting
garnetcbinary, so coverage goes from 76% to 62%.
Per xd009642 on the Unofficial Rust Discord:
it won't followed exec'ed processed by default but the alpha releases have a --follow-exec that will and should work reasonably well ... There's not a lot of people using --follow-exec right now (but there is a handful I know of) feel free to let me know if there are any issues.
I'm vendoring my own tarpaulin binaries so CI don't have to rebuild them each time, so I can just update those.
Under that assumption, your code would have to work with a static foreach kind of construct, so that iteration over the enumeration variants is achieved.
If I'm understanding what you mean correctly, you could represent this at runtime. Just make each enum automatically generate an
EnumType::VALUESconstant that is an ordered array of the enum's values. If your compiler's
constoptimizations are good enough I don't see why this couldn't be just as fast as making it a special case.
The two use cases you've highlighted need to be separated, as they're used in typically quite different contexts.
Exactly, and I'm hoping that having the ability to pack together and unpack the discriminant from a sum type and manipulate them separately we can get the best of both worlds. The "arbitrary discriminant for a sum type" use case is easy, the "set of related integer constants" use case is easy, and with some fairly straightforward screwing around you can write code that translates from one to the other.
The issue with public stub functions is that they have to be output, for the sake of external (foreign language) code where it can't be inlined.
Well, either we need a function or we need to be able to define how structs and tuples are constructed as part of the ABI so external code knows how to do
MyNewtype(3)on their own. The function is the uniform solution, and Rust kinda ends up with the same pattern: most complex types have a
Whatever::new() -> Selfconstructor. Either way, doesn't need to be solved permanently this instant.
Privacy being the default with pub being opt-in is probably the best solution, yeah. I just need to fit it into my mental model of how the language works. It's not the simplest solution, but it's probably valuable enough to be worth the complexity.
Oh wow, that article is utterly amazing. Thank you! It definitely has given me plenty of things to think about, and I need to re-read it again a time or two. I'm still pre-coffee though so I need some more time to absorb it. Two things that stand out to me:
- If we use ZST's for expressing properties of functions, we need syntactic sugar around it. I love the idea and I am very quickly going to get sick of writing stuff like
let add: Pure[NoAlloc[Fn(I32, I32): I32]] = .... It also means that adding traits to a function, ie making the restrictions stricter, is an API-breaking change. (If types are expressed in names via mangling or such, it's also an ABI-breaking change.)
- It also looks like it doesn't compose particularly well. What is the difference between
Pure[NoAlloc[Fn(I32, I32): I32]]and
NoAlloc[Pure[Fn(I32, I32): I32]]? If we proceed only using something like Rust's ZST's then these types are entirely incompatible, and you'll have a lot of functions that do fairly pointless things like juggle
X[Y[Z]] -> X[Z]. I can think of times when that would have actual semantic meaning, but also plenty of times where it doesn't. If you get general enough I bet you could start expressing variable-length lists or unordered sets of properties in the type system, but that brings us to the next point...
- If you go far enough in this direction to be really cool you also start getting into advanced type systems such as linear types, which I'd like to avoid if possible. They're complicated, they're hard to implement and they're generally unable to be checked efficiently, as far as I can tell. (...and I don't like reading type theory papers.) Rust and Zig both avoid these sorts of systems for IMO pretty good reasons, so I would like to find a pragmatic subset of these capabilities that give you most of the cool stuff that people will actually commonly use.
So I like that idea a lot, and want to keep thinking about that approach and persuing it. But it's kind of raw as-is. One thing I'd like is for the compiler to infer the properties of a function, so if you really don't care whether
println()allocates or not you don't have to say anything about it, but if you really care that your function is
NoAllocthen the compiler can prevent you from accidentally calling a function that allocates.
Lots of good food for thought there, thank you.
"Neatly solves the problem and not intuitive" is a great summary of that part of the problem. A keyword might not be a terrible idea, there's a little bit of Feels Bad to it because it actually is just a function but it might be common enough to be convenient. It might be common enough to be worth it though. I'm not terribly concerned about outputting stub functions, on the assumption that they'll get inlined to nothing. Someone else also might want to call that function from outside your program as well, so having it be public even if it's just a stub might still be useful...
...which brings up an interesting thing also worth considering: permission to access the contained value. I haven't thought of this at all, everything is currently just public. However, Rust lets you specify whether the contained value is public or not, which lets you do some occasionally useful things like make it impossible for anyone else to construct a value. I haven't thought about this at all yet, and my first impulse is "just make everything public like C and Python and if someone wants to mark a struct field private they prefix it with
_or something". Then I think about the kind of C code I've seen this result in and reconsider...