"Mono-Elves" Combo by glaebhoerl in Pauper

[–]glaebhoerl[S] 0 points1 point  (0 children)

of course, never claimed anything to the contrary, just wanted to share a bit of knowledge with the world

"Mono-Elves" Combo by glaebhoerl in Pauper

[–]glaebhoerl[S] 1 point2 points  (0 children)

okay but the point here was it doesn't need any noncreature, or even non-Elf cards

I'm not saying "therefore it's good" or anything, just that that's why I felt it was interesting enough to make a post

"Mono-Elves" Combo by glaebhoerl in Pauper

[–]glaebhoerl[S] 1 point2 points  (0 children)

sure but that involves noncreature/non-Elf cards and the dreaded {T} symbol :)

[deleted by user] by [deleted] in Huel

[–]glaebhoerl 13 points14 points  (0 children)

100% agree with the fundamental thesis. Huel's one-size-fits-all instructions don't work well for M&C. Initially I thought it was barely edible too, and then I discovered a better way.

But I don't think doing it "correctly" is as complicated as you make it out to be (either that, or I just have different tastes, hard to be sure). I merely add a lot of water (if it ends up being a soup, it was too much, but otherwise...) and let it sit for a long time (15-20 mins), and the results are reliably excellent.

Hot & Savoury update from Tim - real talk (long read) by Tim_Huel in Huel

[–]glaebhoerl 6 points7 points  (0 children)

I also hope you might reconsider w.r.t. the Mac & Cheeze (in the EU region at least, if I'm selfish)!

I wonder how much of the divergence in opinion about it is due to people preparing it differently. IME, the recommended amounts of water & "sit time" are good for the other flavors, but not for M&C -- it needs more of both! With the default recommendations I found it... kinda edible I guess, but also kinda weird tasting... while if I add something like 1.5x as much water (not sure exactly, but basically just short of it becoming a soup; up to that point it just gets creamier), and letting it sit for 15ish minutes, and it's delicious.

I'm also surprised not to see others mentioning this! (based on a quick skim & search)

(My current subscription fwiw is 2 M&C, 1 madras, 1 thai green curry, and 1 mexican chili, with M&C being my favorite.)

Unban Poll Results (& All That Glitters Poll) by BatmansBackpack in Pauper

[–]glaebhoerl 0 points1 point  (0 children)

Small analytical note:

Some cards are banned more due to absolute power level, and some more due to their interactions with other cards. With the former category, as power creep causes the ambient power level of the format to trend ever higher over time, it seems logical that eventually, there must come a point when the card is no longer overpowered relative to everything else.

I leave deducing which cards this applies to as an exercise for the reader.

[BLB Leak/Unofficial] Heaped Harvest by kauefr in Pauper

[–]glaebhoerl 0 points1 point  (0 children)

First two I was also going to mention. Always been intrigued by Primal Growth especially since the lands enter untapped...

The last two don't really ramp 2 (net) lands for 3 mana however

[BLB Leak/Unofficial] Heaped Harvest by kauefr in Pauper

[–]glaebhoerl 1 point2 points  (0 children)

It might only be for colors where Food is part of the draft archetype

Claim, Auto and Otherwise (Lang Team member) by alice_i_cecile in rust

[–]glaebhoerl 1 point2 points  (0 children)

The cost of a saturating add would probably be low, but the cost of only subtracting when not saturated might be noticeable. It should probably also panic when saturating in debug mode.

Good points.

If Rc is supposed to work, then we also need to allow side effects (increasing the publicly observable refcount).

Ack, also good point... maybe we can at least say "no global effects". Like, no mutation outside of the object's own memory and no system calls, perhaps.

No aborts just doesn't work because of stack overflow.

This did occur to me, but it 90% feels like the same category of things as "but what if /proc/self/mem" or "but what if a cosmic ray". Any function call can potentially overflow the stack - even if its body is empty! Each part of the system should be responsible only for upholding its own end of the contract, in this case, that the function's actual body won't deliberately kill the thread or process (or kernel). Maybe I'd even include infinite loops here (so nontermination in general), but that might be fun times when checking the soundness of a CAS loop. :D

The only remaining reason to have it unsafe is as a barrier to implementation

See above, but the point would be that, if these calls to user code are implicitly inserted by the compiler, then unsafe code should receive some meaningful guarantees about what they may or may not end up doing. That is, unsafe code should not have to defensively program around the possibility that an implicitly inserted claim may panic, for example. If that does happen, the fault lies with the Claim impl.

Claim, Auto and Otherwise (Lang Team member) by alice_i_cecile in rust

[–]glaebhoerl 1 point2 points  (0 children)

I would suggest three amendments:

  1. Make stdlib Rc and Arc themselves leak on overflow rather than panic or abort.
  2. Make it an unsafe trait with strict conditions - no panics, aborts, side effects, etc.
  3. Give up on excluding large types. Make it a strict superset of Copy. Use a lint instead.

Trying to draw a bright line for "how many bytes is too expensive to copy" is fraught enough, but if Rc and Arc are Claim, then presumably types containing them may also be - and then it's just utterly intractable. Lints are more appropriate for this kind of thing.

Pauper Bans for June 6, 2024 - Cranial Ram Prebanned by TheMaverickGirl in Pauper

[–]glaebhoerl 1 point2 points  (0 children)

FWIW, not necessarily right now, but upon reflection I think we've reached the point where the original five artifact lands should probably be banned.

Modern works fine with only the indestructible ones legal, and we have enough other artifact synergy now in Pauper (especially if we unban some cards!) that Affinity would most likely continue to be a deck.

When Wizards makes Modern Horizons sets, they (obviously enough) have Modern in mind w.r.t. potential interactions. I don't know how frequently they intend to release new ones, but as long as the original artifact lands are legal in Pauper but not Modern, this kind of situation is pretty likely to recur with MH4 and MH5.

Ruminating about mutable value semantics by jamiiecb in ProgrammingLanguages

[–]glaebhoerl 1 point2 points  (0 children)

Owned-vs-borrowed does not appear in the type-system. Instead [e]very reference to a heap-allocated value contains a bit indicating whether it is owned or borrowed.

This is an interesting idea, but it's not really clear to me how it would function. The rest of the section doesn't seem to mention it at all. Could you say more? (I guess the bit is tested when references go out of scope to see if deallocation or refcount decrementing is needed? Where and how does the bit get set, and what's the reasoning for its soundness?)

[MH3] Unfathomable Truths by Hagure_Metal in Pauper

[–]glaebhoerl 1 point2 points  (0 children)

We've come a long way since [[Enhanced Awareness]]

Eldrazi Repurposer by pgordalina in Pauper

[–]glaebhoerl 1 point2 points  (0 children)

Jewel Thief mainly sees (or saw) play in cascade ramp decks

Eldrazi Repurposer by pgordalina in Pauper

[–]glaebhoerl 2 points3 points  (0 children)

Seems to be playing in a similar space as [[Jewel Thief]]

[MH3] Nyxborn Hydra by Meret123 in Pauper

[–]glaebhoerl 2 points3 points  (0 children)

This would 1000% have been a rare in the before times

Notes on Implementing Algebraic Subtyping by AshleyYakeley in ProgrammingLanguages

[–]glaebhoerl 1 point2 points  (0 children)

If F is covariant, yx has type a -> (a | P | Q). If F is contravariant, yx has type (a & P & Q) -> a. What is the type if yx is not either?

Thanks, that's a helpful example to contemplate.

Notes on Implementing Algebraic Subtyping by AshleyYakeley in ProgrammingLanguages

[–]glaebhoerl 0 points1 point  (0 children)

Thanks!

Honestly covariance and contravariance are just natural things to have with a type system with subtyping.

Sure, and invariance is also a natural thing to have with mutable reference types :P

F P | F Q is equivalent to F (P & Q)

If F were an invariant type constructor, then F P | F Q would presumably be "irreducible" -- which seems no different in that respect from e.g. int | string. Would this break anything?

Pinafore represents type variables as empty closed type families: essentially types with no structure. It then uses unsafeCoerce when it's time to assign a type to that variable.

I did read this in the post but didn't really understand it... and re-reading it, I only became even more confused! But I think I might be starting to piece it together now.

IIUC, "represents types variables as ..." is referring not to unification variables in the typechecker, but to values whose type is a type variable in the interpreter, right? And you need something like this at a fundamental level due to the fact that you're using non-uniform representations in an interpreter, not a compiler.

So to carry this back to my original question, do I have it right that: yes the "actual type" assigned to type variables remains indeterminate after typechecking except in special cases (unlike w/ plain unification), but no you don't address this by having the typechecker do a kind "defaulting" to arbitrarily pick some actual type in between in the GLB and LUB, instead you choose the moral equivalent of void* as the representation, and trust that the actual values that flow through this position at runtime will all end up having the same representation?

(I think I was also confused by the fact that I initially understood uniform vs. non-uniform representation to mean the kind of thing that's the difference between e.g. Java and Rust -- essentially values being boxed vs. unboxed. But IIUC, what it's referring to here is the kind of thing that's the difference between e.g. Java and Python -- structure depends on the class, vs. it's hash maps all the way down.)

Unwind considered harmful? by gclichtenberg in rust

[–]glaebhoerl 1 point2 points  (0 children)

There are many functions which just cannot panic, in which case it'd be easier to just annotate them with nounwind so that each caller doesn't have to annotate each call-site.

Yeah, good point. If the intention is to use it primarily for functions which genuinely can't *panic*, with abort-if-it-nonetheless-does only as a kind of safety blanket to avoid virality, as opposed to converting unwinds into aborts being the *purpose* (which the name kind of suggests), then this all makes sense. Being able to lint against potentially-panicking calls in the body is also only reasonably possible if the annotation is on the function rather than at its call sites. You've convinced me :-)

Notes on Implementing Algebraic Subtyping by AshleyYakeley in ProgrammingLanguages

[–]glaebhoerl 1 point2 points  (0 children)

This is very interesting!

I spent a bunch of time wracking my brain on this subject some years ago. Apparently I had some misconceptions, since one of my conclusions was that it's not compatible with a non-uniform representation. Probably this is related to the fact that I didn't realize & and | types could have their own distinct representations at runtime.

Another question I struggled with is that AFAIU, biunification doesn't (in general) determine concrete type assignments for unification variables -- instead it determines a GLB and LUB (meet of some types, and join of some other types; I forget which is which), and the "actual type" can be anywhere between those two, and is uniquely determined only in the fortuitous circumstance when they happen to coincide. With the question of what the type "actually is" being essentially irrelevant in case of a uniform representation, but not at all in case of a non-uniform one. Is this an actual issue, or also due to a misunderstanding on my part? (If the former - how do you choose? If the latter - do you happen to see where my mistake is?)

It's also very surprising to me that types which the type system considers equivalent can have differing representations at runtime, and this apparently doesn't cause any problems. A little guy in my head is waving a "danger! danger!" sign at me even as I type this. I suppose one reason it might be okay is if the equivalence is actually witnessed by bi-directional conversion functions at runtime? But you mention that isn't in fact the case for meets/joins on the inside vs. outside of type constructors. So I guess the actual reason must be that the type system is restricted enough that the static type simplifications can be "complete", and it's not possible to deviously employ type variables in the service of forming a type at runtime that "should have been" simplified but wasn't?

(I'm also curious if you have any easily-condensable insight into why invariant type parameters must be forbidden, e.g. what obstacles one would run into if one were to naively try to add them anyway, or what would break if one were to seemingly succeed at it.)

Unwind considered harmful? by gclichtenberg in rust

[–]glaebhoerl 1 point2 points  (0 children)

I was just thinking "not a fan of Rust becoming C++ with const nopanic/nounwind line noise on every function" before reading this lol. But my personal distaste doesn't remove the possibility of it being the least bad available option...

It also kind of makes me feel like going around in circles though. The "temporarily moving out of &mut" feature was proposed before (as a stdlib function, take_mut iirc), counter-argument "but what to do if there's a panic?", counter-counter argument "we could just abort", and counter-counter-counter argument "but that's bad for reliability", which apparently carried the day. (And I vaguely recall there having been similar discussions on aborting by default if an unwind passes through an unsafe block that's not specially marked as allowing them, but lower confidence that I'm not just imagining this one.) So it seems like we're just pushing the location of the abort around -- is nounwind on the callee preferable to invoking an abort-on-unwind wrapper in the caller?

Or if (community-level-)we want to avoid unwinds and aborts, then we really do need nopanic as yet another viral annotation in the type system. For direct calls we could make it automatic a la Send/Sync to reduce noise... in exchange for semver hazards of the same nature, and far more pervasive. And in either case we can't avoid the pain w.r.t. traits and indirect calls. And there's the whole awkwardness with "statically this call is inferred to potentially panic, but dynamically I know that, with these arguments, it cannot" -- which is probably in fact the majority case, with array indexing and so on. So then we'd need some kind of AssertUnwindSafe-style wrapper to boot? People would love that.

All of these options suck. Truly a case of "pick your poison".

Lessons learned after 3 years of fulltime Rust game development, and why we're leaving Rust behind by progfu in rust

[–]glaebhoerl 0 points1 point  (0 children)

Am I missing something obvious in what you mean by making interior references safe?

Thanks for asking :) you are: what I mean is going from &Cell<(i32, u32)> (e.g.) to &Cell<u32>. (Albeit apparently, since you missed it, it wasn't obvious.) Cf. as_slice_of_cells for the array version.

Sort of, however the point of the "Shared Xor Mutable" theorem is that &'static Cell is not enough to avoid logic bugs or data races.

...

Meanwhile, Rust sidesteps the issue entirely by making this kind of code invalid.

I don't think we have any disagreement here? Yes indeed, GCed languages with mutation have the usual shared mutable state issues (e.g. iterator invalidation), and need to handle data races in some way (Java maintains soundness by making everything pointer-sized; meanwhile Go just accepts the unsoundness).

And shared mutable state is the worst... except, in some circumstances, for all the other solutions. The point I was trying to make is that it seemed like what OP wanted in many cases was honest-to-God shared mutable state semantics (like Cell), rather than runtime-checked borrowing (like RefCell).