RIP @googlechrome 2008-2025.
It was a good 17 year run.
Today is the day I'm finally forced to uninstall Chrome.
They finally forced uBlock Origin off by "upgrading" behind my back without explicit authorization when I had to restart my machine, in a fashion I can't just override using developer mode or whatever tricks they've allowed me in the past, and the result is an internet experience that is completely unusable.
I'd forgotten how bad normal folks have it. Fuck this.
I'd be hard-pressed to write something to pass that much of the GCC test suite. There's just _so_ many corners, but really that is more about the ability of a human to stay on task for that long, than difficulty per se.
Two person years to get to the state it is in is actually pretty fair. If I were running a company and that was what we were doing, I'd probably have to devote significantly more of a team to it to have confidence in success, and really I _wouldn't sign up for the task_.
Writing a compiler greenfield from X to Y where you can get benefits from a 80% solution is one thing. A couple of weeks can get you a prototype very fast. I've done this lots of times. But writing one that has to match another system's behavior is madenning stuff. Doing so with the equivalent of a team in an isolated black room without internet access starts to become implausibly difficult.
This is in part just because there isn't a developer good enough to do it that you could corral into doing this thankless task for that long. They all have way more efficient ways to spend the next two years of their life!
This is one of the better AI chip companies, it's like if @Etched were real. It's sad they have "Contact Sales" where a price should be, but their coming Asimov chip makes the correct tradeoffs for low cost transformer inference, and I trust they can tape it out.
Haskell is quite useful whenever you need to manipulate syntax trees, shake-style builds make complex cross-language integrations easier when CMake/Cargo aren't quite up to snuff, nix provides reproducible distributed builds. Not everything is Haskellified, but it helps, and a good cross-section of the team being Haskell literate is a nice edge, allowing us to reach for it as a power tool whenever it is appropriate.
Positron has raised $230M in our Series B!
We're eagerly gearing up for the challenges ahead, and yes, we ARE hiring! Actual humans, in Haskell and hardware roles, remote friendly.
Feel free to reach out to me.
https://t.co/YatyfCfjlu
Started a Champions campaign for my wife and some friends. Apparently the biggest star of the show was a throwaway background character. Meet Chicago Jesus.
https://t.co/SNHo7GiLRx
It is a catamorphism variant. Consider
newtype Fix f = In { out :: f (Fix f) }
cata :: Functor f => (f a -> a) -> Fix f -> a
cata f = f . fmap (cata f) . out
A catamorphism is a way to fold over a recursively defined container, one layer at a time. Aka a 'fold'.
But let's say you need access to all the stuff at one time. you could basically give access to a copy of the structure.
para :: Functor f => f ((Fix f, a) -> a) -> Fix f -> a
But there's nothing all that special about Fix f here. Maybe you have your own special form of fixed point you want to use. e.g. say you're working with [a] and you want to take
data ListF a b = Nil | Cons a b
as the base functor for [a]. Then you might use
zygo :: Functor f => (f b -> b) -> (f (b, a) -> a) -> Fix f -> a
to let you 'roll your own Fix f' for the recursion helper.
zygo then is literally the only sensible thing that typechecks and is left as an exercise for the reader, but with it you can of course define
para = zygo In
https://t.co/ireY1Xr1ti is an ancient article I wrote on the topic back before google abandoned the concept of 'knols'.
The main thing I did with that part of category-extras, the part that became the recursion-schemes library, was show that the common recursion schemes all arose from the use of different monads/monad transformers and comonads/comonad transformers and that you could use a compositional vocabulary based on distributive laws for them to implement everything.
e.g. we can just sort of generalize a catamorphism to allow for the use of a comonad of your choice.
g_cata :: (Functor f, Comonad w) => (forall a. f (w a) -> w (f a)) -> (f (w a) -> a) -> Fix f -> a
g_cata k g = extract . c where c = liftW g . k . fmap (duplicate . c) . out
Then observe that zygo is really just the instantiation of that with an appropriate distributive law for (,) b over an arbitrary functor f.
This led to a more compositional vocabulary for recursion schemes than what had come before. zygo, histo, para, prepro, etc. were already in circulation through various students of Lambert Meertens and folks who realy liked Richard Bird's squiggol formalism.
But this let me break out the 'zygohistomorphic prepromorphism'.
https://t.co/dubUCTSE08
Since then Nicholas Wu, Ralf Hinze, and Jeremy Gibbons pointed out that what was _really_ wanted was an adjunction. Not a distributive law or monads/comonads. This led to papers like
https://t.co/J5znkpXCQk
and in particular this lets you talk about a couple of other morphism types 'mutu' etc. that couldn't be said in the vocabulary above, because those adjunctions don't run necessarily go from Hask to Hask.
But in Haskell back in 2008 when I wrote that code up, that was just a little bit beyond what we could properly express!
I hope that helps.
There's two reasons: In almost all of those cases you'd have to do a 'two pass' algorithm when using the other encoding, one to map, then the other to apply this operation.
And it is a worse model for the theoretical justification, which is day convolution, which works even in monoidal settings where that -> is well defined even for a monoidal category.
The fact that the f a -> f a -> f a version works at all is fundamentally leaning on Cartesian-ness and pushes bad intuition about the purpose of division (admittedly conquer not requiring (a -> ()) leans a bit on "Cartesianness" too!)
The latter issue is a moot point because this isn't generalized over all possible categories and is of course tied to Hask, but the first one was enough to push me to this implementation.
In practice I often want to use it with the sum/product from GHC.Generics though, so I get forced back into two passes!
That only carries you so far. Eventually you need _some_ information from yur object. Unfortunately the language then drives you to things like extra costly *pImpl indirection patterns and the like. Without those tricks details about the class's fields still go in the header, and their size is part of the computational content consumers not only get to but have to know about the class.
That can be a handy trick.
https://t.co/Xek3o8Xgbs and
https://t.co/iYYKdpK6Ko
give examples of two different ways to construct this that I use a fair bit, the linear-logic De Morgan'd version like you used, and the Heyting style version of things, depending on how much structure I want/need to get when looking at the type of refutations.
Maybe a cleaner analogue to what you are doing is how doing field access is like supplying a variant full of continuations to cancel a record full of values, while case analysis is using a record full of continuations to cancel a variant full of values.