Trying to use Mir ion, its a perfect example of the failure of D's attribute system

A moo person moo_mail at fake.com
Thu Jan 19 04:06:27 UTC 2023


On Thursday, 19 January 2023 at 02:10:14 UTC, Walter Bright wrote:
> You can pretty much not use any attributes if you use none 
> (including not using @safe).
>
> We can help if you can present specific cases.

In this case you can't leave them unannotated as all the Mir 
functions are annotated and they try to call stuff in the type 
you are trying to serialize.

@safe and const are the one that is the most problematic in my 
case. (I mentioned @nogc before but I listed it by mistake but I 
have ran into similar issues with @nogc libs)

I am not really looking for help with Mir Ion specifically as its 
just some library, honestly at this point I'm probably not going 
to use it because it's too unwieldy and over engineered.

But here is what I have had to do so far to even try to get 
things compiling. I feel that Mir trying to be @safe is forcing 
me to write even less safe code than I would normally just to try 
and get it through the type system. At a certain point you just 
start casting and annotating randomly just to get it to compile. 
These sorts of workarounds seem to be what always happens when I 
encounter these annotations. In my own code I avoid them like the 
plague if I can't get by without them.


struct PageSerial {
	// this is just some GUI element
	// the contents of this gui element is what I am trying to 
ser/deser
	private Grid g;

	@serdeKeys("blocks")
	@serdeIgnoreIn
	//@serdeLikeList
	auto blocks_out() @property @trusted const {
		// Had to put @trusted because the mir function to ser/deser is 
@safe
		// Had to put const because the mir function decides to cast my 
type to const when working with it?? no idea why

		import std.algorithm : filter, map;
		import std.range : ElementType;
		// have to cast away the const
		auto t = cast(PageSerial*)(&this);
		auto r =
			(t.g).contents.children()[]
			.filter!(c => (cast(GridBlock)c !is null))()
			.map!(c => GridBlockSerial(cast(GridBlock)c))();
		static assert(is(ElementType!(typeof(r)) == GridBlockSerial));

		// trustRange wraps all the range functions with @trusted
		// had to do this because somehow the map type's empty 
evaluated to @system
		// but the range is iterated in the @safe serializeValue so i 
have to force it
		return trustRange(r);
	}


	// this one doesnt even work so not even sure if its set up 
right for Mir
	// at least it compiles but trying to read the data back in 
causes exceptions
	@serdeKeys("blocks")
	@serdeIgnoreOut
	@serdeLikeList
	@serdeProxy!GridBlockSerial
	auto blocks_in() @property @trusted const  {
		struct R{
			private Grid x;
			void put(GridBlockSerial b){ x.addElement(b.makeElement()); }
		}
		return R(cast(Grid)g);
	}
}

GridBlockSerial is another serialized type but not as complex. 
The PageSerial type gets passed into Mir's serializeJson and 
deserializeJson. Each of which pass them down into some lower 
level templates (eg Mir's serializeValue) which is annotated 
@safe.

I try to go in and read Mir's code but its just a spaghetti mess 
of templates, its very hard to reason about and understand what 
is going on. It's like a ton of engineering effort was spent in 
an attempt to make the library "safer" in some type system sense, 
but the net result is a library that is borderline unusably 
cumbersome and actually incentivizes me to write less safe code.

If the argument is that the library should have been designed to 
infer the annotations, then why even have the annotations in the 
language at all if the best practice is to infer them.


More information about the Digitalmars-d mailing list