#Idea for better serialization framework
3760 messages Β· Page 4 of 4 (latest)
People usually say you "walk a tree"
The idea is as the walker walks, it visits things
Would it be like, a full on SeqWalker or a special VecWalker.
It would be a VecWalker so that it can pass the Vec by value/reference if possible (the visitor can accept it), and then switch to using a slice walker (basically the array walker but would be more abstract) as a fallback
Interesting.. ........
What optimizations could you gain from using a ref to the vec instead of a slice?
I was thinking more of a mutable borrow
Exactly what gets passed for each type I'm still not settled on
Will this new serialization framework be zero-copy? No just curious π
I believe frog has said as much
Serde is somewhat zero copy where possible
I do zero copy json quote often
Yes, though probably not as much a rkyv
Ok I see, well if we could have such support it would be great I feel like I really have a use case for myself like I want to use a zero-copy deserializer for my app which also has a great support with actix. If you could provide such support it would be great to have. π
what's the point of seeds when it comes to builders
Is treaty materialized now
It can be used for a few things, one is for configuring the builder at runtime, another is to update/merge a value instead of making it entirely from scratch
im having some trouble with my sequence definition...
pub trait SeqScope<'ctx> {
type Error;
fn visit_next<V>(&mut self, vis: V) -> Result<Option<V::Value>, Self::Error>
where
V: Visit<'ctx, ???, Self::Error>;
fn size_hint(&self) -> Option<usize> {
None
}
}
how does your SequenceScope construct values while just returning Flows n stuff
The scope doesn't build anything, its a special type of walker
In treaty's case that bound you have the ??? on is handled by the provider API
In serde's case the Visitor doesn't have a generic
Ah is this one of those cases I should ask a question about the Builders
Atleast I knew ahead of time that my visit was kind of an abominable cross between the two
What would you say a Walker is to you... like what behavior is it supposed to encapsulate 
And by provider you mean when it's cast into its protocol?
I took the terminology from tree traversal where you "walk the tree", a walker walks a data structure which is a generic form of iteration
Yeah
Walkers provide data
A sequence scope encodes the idea/ability of walking (iterating) a sequence of values that may be different types
In some sense it's a Iterator<Item = dyn Any>
Visitor is taken from the usage of serde where something consumes data from a deserializer
Written using the From/Into traits it's: From<dyn Walker> for dyn Visitor
So let me see if I understand... it's not the same as serde, where the return value lands at the callsite, but rather in the pocket of the visitor, so to speak
So Builder can save the visited value, and return it?
as a visitor
It is the "same" as serde just more generic. In serde a deserializer (deserialize_any) takes a visitor as argument that will receive the value using a method like Visitor::visit_i32. In treaty the deserialize_any is the Walker::walk (roughly) method and visit_i32 is Value<i32>::visit
So i guess I understand that part, I still don't think I understand what it all would expand to. Is this like how you could use it at all?
struct Foo {
id: i32,
name: String,
}
let value = Foo {
id: 420,
name: "weed".to_string(),
}
let other = value.into_walker().walk(/* idk how the visitor would look but yeah */)
Like how would you obtain other, as a modified version of value, if the visitor modified it in some way
struct Foo {
id: i32,
name: String,
}
let value = Foo {
id: 420,
name: "weed".to_string(),
}
let mut builder = <Bar as Build>::from_seed(());
value.into_walker().walk(builder.as_visitor_mut());
let other = builder.finish();
it acts as an out param
do to the need for object safety
if you don't want to consume the value you would do (&mut value).into_walker() or (&value).into_walker()
Is it already usable?
No
I'm currently like 2 layers of crates deep making things treaty needs as dependencies
lol
So ETA for minimally viable product somewhere in 2026 ;3?
thanks for all your cool crates
The grind is real. Also, thanks for your efforts. I think a lot of us are really looking forward to seeing the finished results
i do this too much

So many
What started out as a replacement for serde has become a replacement for all of crates.io
Sorry, but I am still wondering where is the project located and what is it called. π So could you please provide a link to your project?
For treaty?
To be completely honest the repo itself is very not helpful at this point
It's the least documented of them all
So as it stands, treaty is waiting on supply to replace it's lifetime any system. I am also still working on effectful but that one is developed with treaty. I also have parsing and proc macro crates in the works for treaty's proc macros
And supply is being split into 3 crates: type_tag, supply, and lt_any
Frog on their way to replace half the Rust ecosystem:
:(
i need to check whether io is still controlled by uk or not
we recently gave up our claim on the indian ocean island territory
Sorry but I wonder folks is this thread not getting quite offtopic? π
that's what threads are for :3
I thought since there were already plenty of cat pictures, some discussion about colonialism wouldn't hurt too much 
Yes, right but I just had this feeling from seeing all this different topics discussions here as well as seeing that we already have a #offtopic channel for offtopic discussions. So It kind of made me feel like that.
But anyways you folks better know than me (as I am not the mod) π π .
If you look hard enough, you can find other offtopic threads, hidden from the public eye. These usually don't get found by the spammers and trolls, so they stay nicer: that's intentional.
Well Idk about the cat stuff π but I kind of find this community really like safe place tbh like you feel really like at home. While on the other hand I have seen other communities like once I was part of this one webdev community because I also have the skill in web dev. And they were talking about weird stuff like one folk was talking about weird medicine they have to talk and it talks a long time to dissolve I mean why are we discussing these stuff here. Like the community felt really nostalgic to me and also I started having negative vibes just being there.
The only thing I found most here is anime Idk why maybe alot of anime fans here. Though I don't like anime myself (please don't hate me after this one). π
Though, I do fear one thing, like if this community grows it doesn't end up being like other communities though I don't mind change but not the negative/bad one.
Ok I see, that's great π .
No, frankly you were right.
@long sigil, hey, this is a forum thread with a dedicated purpose. In the future, please avoid leaving that purpose behind.
This is actually just due to us not having more detailed moderation guidelines written out anywhere.
(Open issue, we know about it, but it may be a while before we can address it.)
This much is true, but those threads are created to be offtopic.
This one is very much about @thorn badger's very WIP serialization framework
Generally speaking, we still expect people to not detract from the purpose of a thread they did not create.
I should probably start writing down moderation policy somewhere we can bikeshed. I guess I'll do it in a bit.
Don't forget to build off what me and inferno started
Mod v. mod, who will win? (/j)
mod policy built on treaty & lcrust
lets lock them in an arena
Great idea! We can use mine.

arena allocator
how's this going then?
It's going, life has been getting in the way 
life 
Get rid of it
rip life
If GOL is eating your resources and you can't stop it, try rebooting
how's this going then? :3

I'm working on finishing the update to supply
Then I can use supply to replace some of the internals of treaty
:D
Oh no
IMHO i think ssz is great!
?crate ssz
Treaty is much bigger than this type of crate
Oh, sheesh. 367 days already
Happy birthday
What da hell is supply
?crate supply
> 1K downloads
impl<T, L> Reify<L> for T
where
T: ?Sized + Tagged,
L: LtList,
LifetimesTagOf<T::Tag>: FirstN<L>,
LifetimesTagOf<T::Tag>: FirstN<
LifetimesOf<LifetimesTagOf<T::Tag>, L>,
Lifetimes = LifetimesOf<LifetimesTagOf<T::Tag>, L>,
>,
T::Tag: WithLt<LifetimesOf<LifetimesTagOf<T::Tag>, L>>,
{

about the codegen?
how did LLVM literally optimize everything out
its good
it also doesn't always work out
the addition of effectful did not help things
also i had a question about const_transmute
i thought any::type_id_of had some non-equivalence problems
like the type id of T not equaling type id of U didnt mean T != U in some cases
or something
this was always a probabilistic issue
and it is now more or less fixed
since to get a collision takes about 1 type per nano second for the entire length of the universe's lifetime
(its a u128)
oh good
so you wen't with a type erasure type of thing instead of const transmute?
also, how am i supposed to get the ../effectful dependency?
well they are orthogonal things
its on my gitlab
https://gitlab.com/konnorandrews/effectful i still don't know if effectful deserves to exist as a real crate
fair warning I haven't worked on treaty itself in some time so i don't remember if it was left in a working state
i'm not really looking to run it or use it but more to understand what generic programming that optimizes well looks like and works like
lol very readable test error
really the big thing is, dont touch async blocks
or really anything async
i dont even know what async does or works like
That's... actually not probabilistic, but I think we fixed it.
The short version is that fn() -> &'static T and for<'a> fn() -> &'a T used to be subtypes of each other. This, in principle, makes sense, since they're more or less two ways to write the same type.
However, they do not have the same typeid, so you end up with a funny situation where "the same type" has two different typeids.
I think we've since decided those two aren't mutual subtypes, so they're actually just different types and them having different typeids makes sense?
is that the only case or is there infinitely many more?
AFAIK infinitely many, but if I'm right we fixed them all
damn we done infinite work in finite time, that's a supertask
The cased would be the infinitely many different ways to write the same thing once you bring for<'a> into the picture
Just dm froge patches generated with git format-patch 
I'm not going to setup mirrors
nooooo
why not though, it should be trivial to write a hook to sync on commit/release
what's wrong with gitlab?
it being unconventional
I don't want to manage two different spots
good 
almost noone uses it, which makes interacting with it difficult, as I have all my env setup for github specifically
I like gitlab, if you don't, you don't need to interact with it
I remember a guy who has gitlab being a mirror from github without ever needing to touch the former, so should be simple. I'll look into it at some point, will share if some simple script to just procedurally sync the state can exist
Plus I only use GitHub to report issues with other crates
You can just set it in the GitHub settings
yeah github is not great
offtopic, but what is even so good about gitlab, why use it over github?
I much prefer gitlab's UI and user management
Plus I use gitlab at work in addition to bitbucket
plus it's not owned by Microsoft and aggressively pushing ai
Plus I have been using gitlab since 2017
Also they are the only one that named merge requests properly
ikr
This was back when they were the only provider with unlimited private repos
I wouldn't be against that
For the record I don't have an issue with using GitHub, I just choose to not use it for most of my projects
i just use it for the sake of my employer hopefully recognizing github so i get more credit in my future endeavors
i doubt they'll give 2 fucks tho
I have now acquired an issue with using github. How do they not support ipv6 yet?
http://ipv6bingo.com/ smth like that
I think I'm going to give up slightly on async support in treaty, I'm going to just force all the futures to be boxed
That should make effectful tolerable to use
Amazing website
RIP
trying to build docs for treaty rn and it doesn't work due to unresolved imports from effectful, is that something I can fix?
You can get a copy of effectful from my gitlab
It may or may not actually build though
Considering you would need to find the exact commit treaty was using
Or I guess you could try the newest V2 version
With the new supply and ty-tag
Though that one only has the core primitives since I am redoing them
I cloned treaty, effectful, supply and ty-tag, are there specific branches/tags I have to switch to in order to make it build?
well hard for me to say...
I see :D
oh right I have uncommitted edits to supply, ty-tag, and effectful
welcome to my haphazard development of personal projects
treaty has never been in a state where I would even consider it alpha software
rn I'm mostly interested in looking through it and understanding the idea/architecture, it seems quite interesting and I might want to implement something similar in other languages
I can probably explain it better than the code does currently
I have had to explain the idea quite a few times
the core idea is that data transformation can be represented by 2 parts: something that "explodes" a value into a shared schema, and something that "collects" the pieces into a different data structure (this isn't a new idea) treaty's uniqueness is that the shared schema is not fixed, the two parties can negotiate what schema they want to use
treaty has 2 basic protocols (parts of a schema) one is for a single value to be passed from a walker to a visitor (what I call the two parts), the other protocol is to allow a visitor to ask for multiple possibly different values from a walker in a sequence
negotiation happens via supply (in the new version) to allow asking a walker or visitor trait object "do you implement trait X"
a lot if treaty's in code complexity is not actually related to this but to my want to support async
tysm for the explanation! I think I kinda understand the high level idea now, but I'll try to look at the code to get a better understanding of how it works in detail
alright it builds now (and the treaty/examples/demo.rs runs) all the crates are on branch main except effectful which is on branch v2
awesome, thank you!
quick question, why is effectful v2 full of box pins? isnt it supposed to be no-cost?
They gave up on zero-cost async generics

it was the only good control flow framework without any costs, rip
It turned out to be too cursed for them
i wonder if we will ever see a production capable control flow framework in rust
They went from zero to very low cost, IIRC?
Oh, nevermind, box pins
Yeah, they're boxing all futures in effectful to make its API actually tolerable to use
so for one thing some boxing is required for async and using trait objects, and also I just used boxed futures everywhere because the old version of effectful caused all of the tools like RA and rustc to explode with the complexity of it
The blocking mode is zero cost
Which is the one I care about, async I want to have but I'm not concerned with it being blazingly efficient
If someone can figure out a way to do it without killing rustc and be more efficient than boxed futures then I am open to adding it. I made sure to leave effectful's API open to other possibilities
I would also be interested in doing some benchmarks at a later date to see how much overhead the boxed futures have over the blocking mode
Plus a good chunk of the rust ecosystem has more or less decided that boxed futures are "good enough"
Assuming llvm doesn't explode (previously it liked to complain that it was running out of virtual registers while inlining)
seeing that C++ decides to box every coro, I don't think it will be a big deal
corro? 
coroutine probably
indeed
Please refer to them as
utines from now
:3
how's this going? @thorn badger
use supply::prelude::*;
struct Demo<'s> {
s: &'s str,
}
impl<'r, 's> Provider<'r> for Demo<'s> {
// For the example we include both lifetimes even though only 's is used.
type Lifetimes = l!['r, 's];
fn provide(&'r self, want: &mut dyn Want<Self::Lifetimes>) {
// The N<1> is to make it &'s str.
want.provide::<ValueInferTag<N<1>>>(self.s);
}
}
#[test]
fn example() {
// A string that lives longer than demo.
let s = String::from("hello");
let s_ref = {
let demo = Demo { s: &s };
// The N<1> here changes the lifetime of the &str to
// be &'s str instead of &'r str. That way we can return
// the borrow from this scope.
demo.request::<Value<&str, N<1>>>()
};
// This is a reference to s as expected.
assert_eq!(s_ref, Some("hello"));
}
supply works again 
#[test]
fn example() {
let mut demo = Demo { value: 0 };
// Erase the type so the following doesn't know it's a Demo.
let p: &mut dyn for<'r> ProviderDyn<'r> = &mut demo;
// Access demo as a &dyn A to call get_num.
assert_eq!(p.request::<Value<DynA>>().unwrap().get_num(), 0);
// Access demo as a &mut dyn B to call set_num.
p.request_mut::<Value<DynB>>().unwrap().set_num(5);
// Access demo as a &dyn A to call get_num again.
assert_eq!(p.request::<Value<DynA>>().unwrap().get_num(), 5);
}
trait A {
fn get_num(&self) -> u8;
}
#[tag(remote)]
type DynA<'r> = &'r (dyn A + 'r);
trait B {
fn set_num(&mut self, value: u8);
}
#[tag(remote)]
type DynB<'r> = &'r mut (dyn B + 'r);
dynamic traits go brrrr
Please tell me you won't limit some things to only be accessible via prelude
Some crates do this and it really pisses me off every single time
I don't even know how they do it, but there are a few crates that have types which aren't accessible from their actual location, only the prelude
Nice c:
nah the prelude is just reexports
The prelude in those crates is also just re-exports
They just managed to make it re-export things that are available through no other path into the goddamn crate
I mean that I have checked that it's reexporting from the public spots
(the 3 not re-exports are from another crate)
im not sure if im going to keep the tag macro being reexported because it needs you to do
#[tag(root = supply::ty_tag)]
// ...
yeah I was accidentally working on my 0.3.1 branch
rustdoc please
just let it link to the macro's page
i just had an idea when i saw this snippet
does rust have traits as generic arguments?
like ```rs
trait Foo<T>: T {}
impl<T> Foo<From> for T {}```
no
i just saw ```rs
#[tag(remote)]
type DynA<'r> = &'r (dyn A + 'r);
trait B {
fn set_num(&mut self, value: u8);
}
#[tag(remote)]
type DynB<'r> = &'r mut (dyn B + 'r);
and went "why dont you just" ```rs
#[tag(remote)]
type DynT<'r, T> = &'r (dyn T + 'r);``` and realized `A` and `B` werent generic types but rather traits
Current "working" commit set:
treaty: 32d15e4eeaeebfc624977eefaf6be488fb94ed87
supply: 9b23d489f1ffde14d50463cb48ea7114f6048b9d
effectful: 0aeb059df57ab5fbfed91494a306452123329aeb
ty-tag: cc15ca9f789136207bb3eda9d83c2af2ba421e85
And by working I mean you can run cargo run --example demo and get
[src/build/builders/core/value.rs:401:9] value = HasSend(
(
1,
2,
),
)
thread 'main' panicked at examples/demo.rs:48:79:
called `Result::unwrap()` on an `Err` value: missing value of type `demo::S`
so much work to do 
I am once again asking myself if effectful is worth it
Maybe
what's it do
What's the current hell
Allows being generic over async-ness
fn walk<'r>(
mut self,
mut visitor: DynVisitor<'r, L, E>,
) -> ThunkMaybeSend<'r, E, Result<Self::Output, Self::Error>>
where
Self: 'r,
{
Ctx::state((self.0.unwrap(), visitor))
.into_thunk_maybe_send()
.update_maybe_send(|ctx| {
let this = &mut ctx.state.0;
let mut visitor = &mut ctx.state.1;
let x = visitor.request_mut::<meta::Value<ValueTag<ValueSrcSend<&mut T>, E>, (_0, _0, OffsetLen<_1, L::Len>)>>();
let y = x.unwrap();
let x = HasSend(&mut this.0);
y.visit(x)
.map_maybe_send(|_| Ok(()))
})
.no_state_maybe_send()
}
The thunk stuff
lol are you also making it generic over send/syncness?
It has to be to be useful in things like tokio and also support serde
Since none of serde has Send bounds
yeah but damn
Im thinking I should just give up on async support
I was kind of really hoping to use treaty for async, streamed deserialization of a very large list
Like, "hundreds of megabytes" large
If we could come up with a way to make it not destroy the API
Like I could split all the protocols into an async version
Basically what every other crate does
Have one async API and one sync API
I have a feeling like it would be quicker to implement or even wait for someone else to implement effects in the compiler
in terms of development time
what are effects?
yeah but i relate in your pain of how sucky this solution is
I'm not sure if any of the proposals so far would actually be useful in treaty
afaik constness, asyncness, etc. basically allowing you to be generic over different effects
Also fallibility is usually included
That's like the gist of it afaik, but how is it usually implemented?
Or "used" i guess
@thorn badger What problems have you been running into so far?
The way I've heard it it's basically like returning something and then collapsing it to a desired effect
My main one with effectful is how it poisons everything with the MaybeSend dynamic bound
Which currently I need unsafe at every impl site
And every type needs it basically
That's the question with no real answer
And on a lesser note it's annoying that it forces a monadic style on the code
The problem I have with this is people (including me) will forget to support both
what's the caveat with that
^
i mean it would be great to be generic over asyncness, constness, infallibility etc. etc. all at the same time to avoid code duplication
but at some point wont it get so complex its not even worth to implement stuff for all cases?
It's some what unique for treaty since it almost entirely runs other people's code on the walker and builder side
If that user doesn't support both even if they could then their stuff becomes useless for half the API
^ treaty itself is just glue
It's a problem I don't know if I want to deal with since it means likely that treaty's async will be kind of useless
I thought an effects library has been implemented before
There are multiple in rust and many many among other programming languages
They all do it in a different way
Do you have a current favorite implementation in Rust
Am I allowed to pick my own?
No...
Then conrad's
When the readme mentions maybe-impls, is it talking about that proposed ?const/?async syntax
https://github.com/rosefromthedead/effing-mad is a close second
Cuz I agree I think that shit is uggo as fuck
Yes
If I was in the KGI I would say each function should have a default coloring that can be changed/gleaned by compiler inference or through a specific keyword that enables that inference
I mean supporting async is one of fun part of treaty but god the code look hard to maintain... effectful to be more specific even look scary...
my new try with witness proofs seems to be going well
this is tempting and I guess more easy to work with... I mean rust itself currently doesn't have good support for a api that support both sync and async, what you do currently is already pushing it...
could you explain what are you doing right now a little?
so blocking code (in particular anything interacting with serde) doesn't know if it's types implement Send, but async runners usually need Send for any kind of ergonomics. This is a problem effectful has had since the beginning.
The current try is to have types that act as witnesses that a given T implements Send without me needing to write T: Send. We can then compose these witnesses together and transfer them around the code via associated types and generics
type Pause;
type PauseWitness: ProofIsSend<Self::Pause, Self::NeedSend>
where
OutputWitness: ProofIsSend<Output, Self::NeedSend>,
StateWitness: ProofIsSend<State, Self::NeedSend>;
This is a Pause associated type where we can prove (via a witness type) that it implements Send if we can be given a witness that Output and State implement Send
so any type that have a witness is send and hence we can use it in async? ok so how does this work internally? I mean how the same code base can be used for both Send (hence async support) and non Send types?
For any concrete type it can be done via
/// Witness of `IsSend` via `T: Send`.
pub struct SendWitness(());
// SAFETY: Because of the T: Send we know T implements Send.
unsafe impl<T: Send, F: Bool> ProofIsSend<T, F> for SendWitness {}
which witnesses an explicit T: Send bound
fn thing<'a, Env: Environment, T, W>(x: T) -> Thunk<'a, Env, i32, u8>
where
T: core::fmt::Debug + 'a,
W: ProofIsSend<T, Env::NeedSend>,
{
Ctx::new(x, 0)
.thunk()
.with_witness::<W, _>()
.update(|ctx| {
*ctx.state += 1;
dbg!(ctx.output);
Ctx::output(42).thunk()
})
.update(|ctx| {
dbg!(&ctx);
Ctx::output(ctx.output + 1).thunk()
})
}
code taking a generic needs a witness passed in with the form W: ProofIsSend<T, Env::NeedSend>,
the Env::NeedSend says if we need the proof or not
ProofIsSend<T, No> is trivial for all types
since we don't have to prove anything
a ProofIsSend<T, Yes> needs something to know that T: Send
/// Wrapper that implements `Send` if the `Witness` proves `T` does.
#[repr(transparent)]
pub struct SendShim<T, Witness = SendWitness>(pub T, PhantomData<fn() -> Witness>);
// SAFETY: The witness proves that T: Send.
unsafe impl<T, Witness> Send for SendShim<T, Witness> where Witness: ProofIsSend<T, Yes> {}
we can then regain a true Send bound via a shim if something acts as a proof
let send_capture = SendShim::new_with_witness::<CaptureWitness>(capture);
SendShim::new(Box::pin(async move {
let ctx = FutureShim(pause).await;
f(send_capture.into_inner(), ctx)
}))
like here is some async code that has a CaptureWitness that knows that capture implements Send (it has CaptureWitness: ProofIsSend<Capture, Yes>) it uses this to wrap it in the shim so the async block is send
error[E0277]: `Rc<{integer}>` cannot be sent between threads safely
--> src/lib.rs:515:35
|
515 | let x = thing::<Async, _, SendWitness>(Rc::new(43)).into_future().await;
| ^^^^^^^^^^^ `Rc<{integer}>` cannot be sent between threads safely
|
= help: the trait `Send` is not implemented for `Rc<{integer}>`
= help: the trait `send_proof::ProofIsSend<T, F>` is implemented for `send_proof::SendWitness`
error[E0277]: the trait bound `(): send_proof::ProofIsSend<_, send_proof::Yes>` is not satisfied
--> src/lib.rs:515:35
|
515 | let x = thing::<Async, _, ()>(Rc::new(43)).into_future().await;
| ^^ the trait `send_proof::ProofIsSend<_, send_proof::Yes>` is not implemented for `()`
|
= help: the trait `ProofIsSend<_, send_proof::Yes>` is not implemented for `()`
but trait `ProofIsSend<_, send_proof::No>` is implemented for it
if you try to use an invalid proof you get an error like these
to actually write code as a user you end up using a thing like an iterator/future chain (a monad) which abstracts away the async-ness
Ctx::state((SendShim::new_with_witness::<Witness>(self.0.unwrap()), visitor))
.thunk()
.with_witness::<SendWitness, SelfWitness>()
.update(|ctx| {
let this = &mut ctx.state.0;
let visitor = &mut ctx.state.1;
let x = visitor.request_mut::<meta::Value<ValueTag<&mut T, E>, (_0, _0, OffsetLen<_1, L::Len>)>>();
let y = x.unwrap();
y.visit(this)
.with_witness::<VisitResultWitness<MutWitness<Witness>>, SelfWitness>()
.map(|_| Ctx::output(Ok(())))
})
.no_state()
.with_witness()
A real example from inside of treaty
I hope no one actually has to write code touching this outside treaty
only if you want to make a custom format that supports both async and sync
since thats the only time code needs to be abstracted over it
okay, so this is pushed to the format ((De)Serializer) side, not the type side, got it
treaty's macros will handle the type side
(since they count as a format)
thanks for explaining
and wow, tbh I'm speechless the implementation is so clever, but tbh if any of these are user facing users will have a lot of problem to actually understand what to do
the price I pay for flexibility is complexity
, I can minimize it but not eliminate it
if we tossed out lifetime preservation and async, treaty would be much closer to serde in complexity
oh also I guess if I made everything use futures
but then I loose good codegen for blocking
so without that most of the time the experience of using treaty should be somewhat similer to serde right? derive a struct and use it
but if a user want to work with custom types they have to dig deeper...
A normal user (not making their own custom format or adapters) would just add a macro to their types and call a function and everything magically happens
the single function being basically
let new_thing = transform(old_thing).unwrap();
since treaty doesn't distinguish between serialize and deserialize or format vs rust types
and ```rs
let new_thing = transform_async(old_thing).await.unwrap();
oh so treaty don't actually have the concept of serializing and deserializng?!
just transforming between types?
yep
its a anything -> anything mapping system
It also supports passing an unlimited number of lifetimes through the transformation (unlike serde's 1 for deserialize)
man the more I hear you talk about treaty the more I want it π₯²
and of coarse no data model is given
you can pass literally any rust type between the two sides
and perform any logic
so it do way more then what serde does which is (de)serializing one type to another...
it can actually be used to transforming normal types between each other as well...
let mut my_str = String::from("hi");
// We make a waker that will emit the my_str to the visitor.
let walker = ValueWalker::<L0, MyTag, _>::new(my_str);
// We make a builder that will accept a String (tagged with MyTag);
let mut builder = ValueBuilder::<L0, MyTag, Blocking>::from_seed(())
.into_ctx()
.output;
// We make a visitor from the builder that the walker can emit into.
let visitor: DynVisitor<_, Blocking> = builder.as_visitor();
// We walk the input data. In this case this just gives ownership of my_str to the builder via the visitor instance.
let _ = walker
.walk(visitor)
.into_ctx()
.output;
// We finish building the String (we check we got a value).
let z: String = builder.build().into_ctx().output.unwrap();
// Print the my_str that passed all the way through the transformation.
dbg!(z);
here is an example I use for testing (this is what the transform function does internally)
it can also convert one "format" to another without an in between
so like json -> toml
the other key design goal for treaty is that all serde compatible code just works with it
so I have (the framework for) adapters for serde serializers and deserializers and types
without that there is 0 chance treaty could gain any market share
exactly, its hard to replace one of the earliest and biggest libraries of rust...
I have a lot of hard goals for treaty
I mean I doubt treaty will any time soon replace the serde even with the serde support... but this will ease the changing process easier
though the core design is actually still basically what my first comment of this thread proposed, its just all the details of how to actually get rustc to accept it
I think of treaty as a proof of concept with the possibility of real use
one thing I think I could make a killer feature for it is to have automatic emitting of struct docs so you can write a struct to toml and have docs with it
yeah that would be a fun thing... but doesn't that feel a little out of place for treaty..? I think of treaty as the library doing the transformation between x and y... docgen seems a little strange here... maybe instead add a way to give extra info to a type that then we can use to do all this cool stuff with?
this would be in the macro (like serde's #[derive(Serialize)]) treaty's #[derive(Walk)] can basically implement as much as bevy's reflect since if the visitor/builder doesn't support the meta info it just gets optimized out
the toml stuff just has to accept the comments meta data and write them out
basically I can treaty rust code as itself an information format
yeah some kind of metadata in here would be nice, I can see other uses for treaty if it extract metadatas too...
like someone may use treaty to do something like what clap is doing... generate a command line interface with helps just from a struct and then parsing it back...
this actually should be totally possible! (and actually way more useful)
treaty is so many things, the only thing it try not to be is a crate that we can actually use 

give me another 50 years (like fusion)
once I have an alpha put together development should be faster
exactly, once its out the wheels start spinning so much faster!
thank you for the work you are putting btw... it was a blast understanding what you trying to achieve
I enjoy melting peoples brains with my cursed code
well ngl, if it wasn't for you actually explaining what you try to do with the code I wouldn't even dare to look at it (I'm still scared to look at Effectful as matter of fact)
are you planning on providing a proc macro such that these bounds and traits and associates type information is all implemented automatically on types that support them?
yes
is it a proc macro or is it a declarative macro that you use yandros crates for derive syntax
this type of "associated type" and "proof trait" programming is probably well-defined but in practice it occurs in so little contexts people dont get the chance to get used to them, if i had more occasions where i could implement stuff using those i probably could remotely understand what your code does xd
everything right now is a procmacro because I don't want to deal with parsing generics
lol 
when i first saw treaty the #[derive(Foo!)] stuff was wacky
now i forgor the yandros crate name lol
@thorn badger why can't you use RTN for this stuff
Don't say "because it's unstable": https://github.com/rust-lang/rust/pull/138424
RTN cannot be used to bound the output futures from calling AsyncFn* traits yet
Aww
Not sure how that would help
@thorn badger it's literally supposed to help with the Send-bound problem?! I mean I only got so far into trying to figure out what the hell you're doing so 
The issue is I need the Send bound to sometimes exist and sometimes not exist
For the same code
Oh it's that also, since it abstracts away async-ness
great
so like
Rust now supports AFITs and RPITITs, but we currently lack the ability for users to declare bounds on the anonymous types returned by such functions at the usage site. This is often referred to as the Send bound problem, since it commonly affects futures which may only conditionally be Send depending on the executor that they are involved with.
is there no way you can turn this abstraction into an RPITIT or AFIT
and allow the sendness to happen at usage sites
These are nested many levels deep in treaty's code
And involved unnameable closure types
And unnameable async blocks
Don't worry it is now "solved" at least more solved than it was
The witnesses work really well
Nice :D
@thorn badger hm, is ty-tag really necessary?
It's what powers my lifetime enabled type erasure
It can erase types that aren't 'static
Yes
but then you mention that the problem with such erasure is that it only allows erasure if all lifetimes are a single 'a
but that doesn't seem instrumental
That's an "arbitrary" limitation of all implementations I know of
right
Namely better any
I'm not sure what you are trying to poke at
uhh
Is the question why I don't use a single lifetime type erasure?
Let's go with
#[must_use]
#[inline(always)]
pub fn of<T>() -> TypeId
where
T: ?Sized,
{
trait NonStaticAny {
fn get_type_id(&self) -> TypeId
where
Self: 'static;
}
impl<T: ?Sized> NonStaticAny for PhantomData<T> {
#[inline(always)]
fn get_type_id(&self) -> TypeId
where
Self: 'static,
{
TypeId::of::<T>()
}
}
let phantom_data = PhantomData::<T>;
NonStaticAny::get_type_id(unsafe {
mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data)
})
}
"why isn't this the entire thing?"
You must make sure that the lifetimes aren't changed when down casting
And of needs a 'static T
A TypeId represents a globally unique identifier for a type.
yes, that isn't std::any::TypeId::of
Notice your get_type_id implementation
yes.
are you saying we're talking about some hypothetical type which can never be lifted to 'staticever
not even via the lifetime transmutation?
I'm saying your example code is flawed
Not that the idea is
It is not.
It does what it is intended to do.
It does not happen to suffice for your desires, but not because it fails at its task, merely because it deliberately ignores something you try to encode.
wait the static bound on self adds an implied bound, ignore me
nah I had code like this in the original treaty
in the grand scheme of things its not that bad
I am basically going through your API surface and identifying how many levels of stretch goal treaty is on by now
<_<
and supply is obviously core to the entire thing, and it depends on ty-tag
but effectful has me going "hmm"
effectful exists only to satisfy the want to support streaming formats
Okay cool.
it abstracts away the async-ness
while preserving perfect code gen for non async code
at the price of adding a whole monadic structure to the code
I haven't actually added any goals since the original proposal
its just the ever expanding needs to support them
then I suppose I'm reinterpreting the original proposal with a leaner set of goals in mind.
if we change the goals then indeed treaty doesn't need to look like it currently does
im going for the "best" a serialization framework could be to my knowledge
trying to imagine if there's a way that effectful and the rest could be segmented out or if they're forever married in API surface
effectful is already it's own crate
well right now treaty depends on it
if you mean so that treaty doesn't have them in it's public API no
it can't happen without giving up something
if someone can devise a way it could be I would be amazed, since this is the result of almost 2 years of thinking about the problem
I'll take a look and start asking around
you're no slouch by any means
but there are people who have been thinking about this problem for 5 years
we could go back to when effectful and supply (and ty-tag) all lived inside treaty's repo
Well, supply being its own thing and treaty depending on it makes sense to me
since it's basically a reimplementation of a stdlib API
that was
yeah but I have been working on this specific problem almost every day
I doubt it would have ever been more than just error in std
std doesn't need it beyond that
I mean yes
I have painted a lot of corners let me tell you 
I'm basically saying that the only reason it shouldn't be in std is the lang might need to evolve a bit further for it to approach its Final Form, API-wise
because like
...
sufficiently generically useful APIs should... exist in std...? if we know what they should look like?
I mean the entire point of having a lean core library is that it should still provide basic tools, right? 
surely T-libs-api won't
anyways
moves along
if someone has a way to improve the design without throwing out my goals then im all ears, if peeps do want to throw out some of the goals then the code is always available to make something else with
Maybe you can post on reddit :3
get more eyes on it idk
to help
if someone else wants to sure, i don't want to use reddit
plus thats something I would only do once I got to an alpha
I mean could ask the rust team or the other ppl to help idk :3
helix already convinced me to rewrite ty-tag (which is now done) so I would rather it be some other API 
im back on for async support
witnesses fixed my issues
at least the ones I had
so now we are back to uncharted territory
still dont know if using effectful will make other people go crazy or not
but π€·ββοΈ
the price to pay for async 
Ctx::state((self, walker))
.thunk()
.with_witness::<SelfWitness, SelfWitness>()
.update(|ctx| {
let (this, walker) = ctx.state;
if let Some(walker) = walker.request_mut::<meta::Value<
&mut dyn Hint<L, ValueTag<TagOf<&mut T::Tag>, E>>,
(_0, _0, OffsetLen<_1, L::Len>),
>>() {
dbg!("got walker");
walker.hint(DynVisitor::new(*this))
} else {
Ctx::output(VisitResult::Control(Flow::Done)).thunk()
}
})
.no_state()
its not that bad 
async {
if let Some(walker) = walker.request_mut::<meta::Value<
&mut dyn Hint<L, ValueTag<TagOf<&mut T::Tag>, E>>,
(_0, _0, OffsetLen<_1, L::Len>),
>>() {
walker.hint(DynVisitor::new(self)).await
} else {
VisitResult::Control(Flow::Done)
}
}
is the async version
wha is this anyways
also wdym :3
im curious wha nightly features would make this easier/make this project easier
I don't know of any
nightly vs stable hasn't ever been an issue on this project
see forwarded message
o oki :3
Also last question what all the differences between Serde and Treaty anyways?
the two main ones are that treaty has no set data model, and treaty supports streaming (async)
along with treaty supporting arbitrary lifetimes and not distinguishing between serialize and deserialize
wha else :3
treaty not distinguishing between de/serialize, means that you can use it to directly convert between formats without having to go through an in-memory intermediate value like you would have to with serde
It's actually looking better
Gotta do something about that dyn Hint though mate needs an alias
Yay :D
One question, I find serde very dependent on recursion and as a result can use a lot of stack
This can lead to stack overflow when (de)serializing some big types... Will treaty perform better on these scenarios?
I have thought about it, and currently I haven't put together a protocol for it. It can't be no_std so can't be part of the normal set
What I may be able to do is actually make an effectful environment that does it seamlessly
Since the async one is close other than all the poll functions nesting
That would be nice... Currently the only good workarounds for it in serde is to
- actually increase the stack size which isn't the best way specifically if the code that depends on serde is a library that users use
- Use crates like
stacker(or more specificserde_stacker) which are ok and get the job done, but are also so hacky that I don't feel comfortable using them...
Have you checked miniserde to see how they are doing it without any recursion?
It uses an explicit stack with type erased visitors
Meaning it needs alloc
So if you were to add no recursion (or at least not crazy like serde) it'll be default in no no_std env or opt in?
It would need to be opt in
it ruins my perfect code gen
because of the allocations
Even if it's opt in it would be still so useful...
Sometimes we can't just optimize size of types that we (de) serialize...
For example when we are interacting with external APIs... A good example is telegram api, it's filled with useless deprecated stuff that they just don't get rid of (because they don't want to break old bots) serde have problem with it... Having this could help there...
No recursion on no-std sort of can't be default
There's nothing else that could obviously be used as a stack
No recursion and you pass in a block of memory to be used for stack space would be workable
So true I understood what that meant
.
This question should not be asked 
Ok
I have been quite busy with work so haven't worked on it much
This weekend should be a good time to though
Treaty is something to admire from afar and then feel sad because we won't see it anytime soon
self fulfilling prophecy
treaty is a psyop to make developers sad whenever using serde even though it's good enough and the rough edges can be worked around
Im sure that the frog will bless us soon enough 
Maybe an alternative solution to "visitors are limiting as fuck" can be baked in the interim
Given that I'm reasonably sure serde's visitor could be replaced by an enum, yes, it probably can
Reasonably sure?
I have some concerns around MapAccess and I never tried it in practice
I think I've approached the enums replacing visitors idea before and it didn't totally pan out but I didn't try that hard either
So, as it turns out, you can make it all work, but at the cost of replacing the visitor with a gigantic pile of generics. Also, you still need to write a trait impl since no generic closures
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=77b6697aad39e206c01c90c6fbee2580 is a nearly-functional impl. I got tired of writing it so I didn't fix the fact the error types don't match (not sure how you'd fix that, but there's probably a way. I mean, serde internally has to solve it somehow)
Amusingly, the fear I had around MapAccess did not actually pan out
Here is the visitor trait if it wasn't horribly long. It is now horribly generic instead. Improvements! (?)
lol
Don't steal my thunder
/s
I'm still trying to work out a good way to have both the recursive visitor and a custom stack visitor
it's ok I'm cooking something different rn that's probably pointless but still taking up all my time
I'm kind of a brainlet though so it's like a caveman trying to carve a stone wheel out of teeth
Custom stack visitors would be so freaking useful...
I hate when serde gives me stack overflow when the type I'm working with is so complex...
Fasterthanlime reference
Hey, it's been a while since you gave us some updates about treaty...
So any news
Still working on supply and since I can't currently type it's going to be a little bit before major update
What happened?
Broke bones in my arms
π
See, this is why I want a mainstream easy to use neural computer interface developed
(For me, I can just lie down in bed tired, and never have to move my arms or anything. So relaxing)
Get well soon please, I want me some treaty updates 
But seriously take it easy for some time... Hope you get better
unbreaks the frogs armes
plz get well soon

One arm didn't get a cast 
ngl it would be funny if you had two full-arm casts
but then you likely couldn't go to the bathroom by yourself which is less fun
frog would not be proud
is frog part of the "as" haters club? 
one day :3
Also wtf is a facet
cc: @thorn badger how does facet compare to treaty :p
π whats going be the actual difference :p
Well facet is going to do a lot of things... Not sure how it's going to work out...
On the other hand treaty is supposed to be focused on one thing mostly
does facet support lifetimed downcast
only single lifetime afaik (that is with peek, which is read-only, but i guess you could implement it for owned values on your own and it'd be safe)
is this dead or?
yes
@thorn badger Do you plan to continue this sometime, or is it forever unfinished?
It's a case of I'm wanting to do other stuff. I did a lot of theory crafting but I don't at the moment have the drive to create and maintain a final product
That's understandable! There's a lot that goes into this, and the last thing you want/need is burnout
Like at the moment I have been focusing on the design of a code formatter
Plus it doesn't help that every time I talk about it everyone becomes confused by why it's needed and the cursedness that it entails. I'm not sure if a full implementation would actually be used by people
What I'm taking from this is, wanting to focus on other stuff / not currently having the drive, doesn't necessarily mean never, just you need a really good break (am I interpreting this correctly?)
That's a good point
I haven't even managed to sell people the current version of supply 
An actual tool on the other hand is something I can just be like "look it does the thing, use it"
Also the existence of facet makes the space a little crowded
I would summarize as, I have completed the white paper, now someone go make the product
It's a little sad, cause I don't feel facet (no offense to Amos) would be as optimized as yours (I could be wrong, this is just my initial unprofessional opinion)
I don't think I can argue against facet in a way that is actually important to most people
I'm also worried that treaty would cause compile times to 10x
Which isn't something that can be really tested until everything is done
At a base level I kind of feel Rust isn't ready for such a crate
It's possible, but only by abusing and building so much on top that it stops being reasonable
I could toss out features like async support, but then it's not the library I want to make
Like with my limited experience with lean, I think I could make the equivalent of treaty in like a single file
Plus I have had multiple times where I set everything up over weeks to only hit a rustc bug that means rewriting everything again
I just don't want to fight the compiler right now
I'm ahead of my time 
At a base level I feel like I'm not ready for such a relationship with madfrog
Can we get a rust2? Then I can make it 
We just need to keep an eye on which language will be the next big thing so I can make the serde equivalent for it
The other part is I actively work full time as a software engineer so I am basically saturated writing code already
ftl has had it too good for too long
And my work code is code I actively like working on so it's not a case of "it's just random code I happen to write"
"A farmer hired a man to work for him. He told him his first task would be to paint the barn and said it should take him about three days to complete. But the hired man was finished in one day. The farmer set him to cutting wood, telling him it would require about 4 days. The hired man finished in a day and a half, to the farmerβs amazement. The next task was to sort out a large pile of potatoes. He was to arrange them into three piles: seed potatoes, food for the hogs, and potatoes that were good enough to sell. The farmer said it was a small job and shouldnβt take long at all. At the end of the day the farmer came back and found the hired man had barely started. βWhatβs the matter here?β the farmer asked. βI can work hard, but I canβt make decisions!β replied the hired man." - Someone
I feel like this quite often (one of my favorite)
rustfmt with new architecture??
Yeah the idea is to write a universal code formatter that handles broken syntax, nested languages, special cases, and a few other things
Motivation being I dislike rustfmt in the edge cases
Yeah I want this formatter to be able to format doc comments and their code blocks all in one go
As always I don't know if I can reasonably do this, but this is the task I have set out to think and research
have you seen topiary? that's basically what it is, it uses tree-sitter to define queries to format syntax nodes, handles broken syntax because that's just how tree-sitter works, and defining formatters is extremely easy: no need to write a parser, just write queries
Though, it's totally a WIP
I want to see entirety of tree-sitter ported to Rust
yes
It's one of the prior arts that are on the list to review
Every tree-sitter grammar is a huge 100k lines of code C file. and you need the tree-sitter runtime which is also a whole lot
haven't
initial support for nested formatting
Resolves #XXX
References #400, #568, #579
Depends on #ZZZ
Description
[PR Description]
Checklist
Checklist before merging, wherever relevant:
CHANGELOG.md ...
yay
Currently my main question to answer is how to represent a document as a data structure that makes it easy to work with (parse, rearrange, insert, remove) and for an optimizer to decide the final layout for
I checked it out. i think the benefit of tree-sitter is that defining grammars is so easy, so you totally got that right.
I have settled on a red green tree but it needs more than just that
it's nice that you can do it in rust instead of C as well
did you already know what a red-green tree is or did you find it out during research?
I had heard of it but didn't really know what it was
i think easy is kind of relative... i find tree sitter to look way more complex and it also has alot of pittfalls writing the grammar...
plus recovery is ass
The prettier paper was an interesting read, but I want more expressiveness than it's document model allows
and no ast
yea i am doing alot of analysis on the grammar
so the proc macro can error
e.g. if lef recursion
At this point I have a list of about 30 papers/resources to read
and bounded recursion gets untangled automatically
yea man red green trees are something else
it took me so long to even understand how they work at all
how do you know all of these data structures? leetcode?
i know them from making this thing
cst's are nowadays represented as red green trees
Oh, hold up, from intuition: is "red green tree" basically just a tree but syntax errors are their own node?
see rowan/roselyn etc
I happen to work researching large scale optimization, so I know quite a few graph things and decision algorithms
This is what rust compiler does, typst compiler
Every time you have something like an enum Expr there is a variant called Expr::ErrorGuaranteed which just means we failed to parse it for some reason
But it doesn't abort the whole program
At this point I want to explain it as the green tree is where all the relations point down towards the leafs, and the red tree is where the relations point up towards the root and a separate relation from every red node to its green node
this is a good read
if you are interested
It's like a doubly linked tree but having been separated into two trees
And yeah the rust analyzer docs is what really made it click
Of these, only GreenNodes store the actual data, the other two layers are (non-trivial) views into green tree. Red-green terminology comes from Roslyn (link) and gives the name to the rowan library. Green and syntax nodes are defined in rowan, ast is defined in rust-analyzer.
Syntax trees are a semi-transient data structure. In general, frontend does not keep syntax trees for all files in memory. Instead, it lowers syntax trees to more compact and rigid representation, which is not full-fidelity, but which can be mapped back to a syntax tree if so desired.
the excerpt
I think focusing on how the red tree is usually built on demand distracts
makes it sound like it's just a tree + something
My active goal is to try and combine a red green tree with an ECS (EC part)
Yeah
seems like a decent fit
My green nodes conceptually have lots of possible annotations
And I see a need for the red nodes to have a similar thing but only while it exists
i read it! thanks, fun!
I've actively been waiting for it, rip
I'm sorry ryos 
wait really
Dependent types are nice aren't they
Until somebody abstracts everything to oblivion
o7
@thorn badger how is the formatter going?
Also I haven't even looked into the difference between facet and serde tbh what the diff
Still in the design phase
I think we get GTA vi before treaty :(
Frog has stopped working on it
How about noww


