tag:blogger.com,1999:blog-2744072865491516720.post2276960050270114230..comments2023-05-03T06:35:33.259-04:00Comments on Higher Logics: First-Class Slots for .NETSandro Magihttp://www.blogger.com/profile/05446177882449578817noreply@blogger.comBlogger3125tag:blogger.com,1999:blog-2744072865491516720.post-91646724034098333942013-11-21T10:42:41.895-05:002013-11-21T10:42:41.895-05:00Thanks for the detailed answer! You just gave me a...Thanks for the detailed answer! You just gave me a lot to think about here :)Mauricio Schefferhttps://www.blogger.com/profile/15247972578064164206noreply@blogger.comtag:blogger.com,1999:blog-2744072865491516720.post-73199031869153248072013-11-20T23:29:34.613-05:002013-11-20T23:29:34.613-05:00"So I went for a mutable variant, but that of..."So I went for a mutable variant, but that of course requires a mutable object, and it lacks the composability of immutable lenses. Still a useful abstraction."<br /><br />I think it depends on how you exploit mutability. If you haven't already, read up on immediate mode UIs. They build a UI representation from a model in a continuous, imperative loop.<br /><br />You could adapt this to the lens concept by simply constructing an immediate mode counterpart of your lens algorithm. Lenses are algebraic representations of updateable views, and immediate mode is a coalgebraic representation.<br /><br />Coalgebraic representations are much more natural in OO languages like C#, and thus they compose well, but it takes awhile to start thinking coalgebraically because algebras are so ingrained.<br /><br />"You could pack it as existentials, but then you'd lose type information."<br /><br />I think any solution for .NET will have to hide some type information in some way, simply because C# types aren't expressive enough. As to how to do this, you can hide the types behind a well defined interface. ORM hydrate is essentially:<br /><br />Func<Source, T><br /><br />ORM flush is essentially:<br /><br />Action<T, Sink><br /><br />Then a Foo:<br /><br />class Foo<br />{<br /> public int Bar{get; set;}<br /> public string Something;<br />}<br /><br />has hydrate/flush ops:<br /><br />static Func<Source, Foo> Hydrator()<br />{<br /> return source =><br /> {<br /> var x = new Foo();<br /> x.Bar = source.Int32();<br /> x.Something = source.String();<br /> return x;<br /> };<br />}<br /><br />static Action<Foo, Sink> Flusher()<br />{<br /> return (x, sink) =><br /> {<br /> sink.Int32(x.Bar);<br /> sink.String(x.Something);<br /> };<br />}<br /><br />You can make a combinator library to build up the hydrate/flush functions incrementally too (there are functional pearls covering this), because delegates can be combined on .NET:<br /><br />static Action<T, Sink> FlushInt32<T>(Func<T, int> getter)<br />{<br /> return (x, sink) => sink.Int32(getter(x));<br />}<br /><br />static Action<T, Sink> FlushString<T>(Func<T, string> getter)<br />{<br /> return (x, sink) => sink.String(getter(x));<br />}<br /><br />static Action<Foo, Source> Flusher()<br />{<br /> return Func.Combine(FlushIn32(x => x.Bar), FlushString(x => x.Something));<br />}<br /><br />But really all the above is doing is performing a coalgebraic transform on an object graph into some serialized form. We can make this a fully generic transform, and in fact, I already did which is my type-safe generic reflection abstractions under <a href="http://higherlogics.blogspot.com/2013/03/sasa-v094-rc2-released-sasadynamics.html" rel="nofollow">Sasa.Dynamics</a>.<br /><br />Start with a low-level reflection interfaces, one for folding over an object:<br /><br />interface IFold<br />{<br /> void Field<T>(T value, FieldInfo info);<br /> void Property<T>(T value, PropertyInfo info);<br />}<br /><br />And one for unfolding:<br /><br />interface IUnfold<br />{<br /> T Field<T>(FieldInfo info);<br /> T Property<T>(PropertyInfo info);<br />}<br /><br />Then you need to perform a type case on the basic primitive types:<br /><br />interface IReduce<br />{<br /> void Bool(bool x);<br /> void Int32(int x);<br /> void Type(Type x);<br /> ...<br /> Object<>(T x);<br />}<br />interface IBuild<br />{<br /> bool Bool();<br /> int Int32();<br /> Type Type();<br /> ...<br /> T Object<T>();<br />}<br /><br />Then you can write a single piece of code that can fold any object graph into a serialized format, and perform the corresponding unfold from the serialized format back into an object graph.<br /><br />Foo, from above, would serialize like so:<br /><br />typeof(Foo)<br />+-Property(Foo.Bar)<br />+--typeof(Int32);<br />+--Int32(Foo.Bar);<br />+-Field(Foo.Something);<br />+--typeof(String);<br />+--String(Foo.Something);<br /><br />(+- is stack depth)<br /><br />You can see that all the relevant type information is present, so you can unfold the structure safely as well. Obviously you also need to handle recursive structures and other special cases like arrays and Nullable structs, but it's totally doable.<br /><br />I recently realized that Sasa.Dynamics is essentially scrap-your-boilerplate generics as found in Haskell. They take the same approach using polytypic folds/unfolds as the foundation of reflection.Sandro Magihttps://www.blogger.com/profile/05446177882449578817noreply@blogger.comtag:blogger.com,1999:blog-2744072865491516720.post-27206519910232574692013-11-20T21:52:53.131-05:002013-11-20T21:52:53.131-05:00I've used a similar abstraction which I called...I've used a similar abstraction which I called "mutable lenses". Initially I tried to use real immutable lenses, but the boilerplate required to define lenses in C# is astounding. So I went for a mutable variant, but that of course requires a mutable object, and it lacks the composability of immutable lenses. Still a useful abstraction.<br />I also experimented briefly with an ORM prototype based on this, but I quickly ran into the problem of having to pack all the lenses for a class together (e.g. in a list), something that's seemingly impossible to do in C# as it would require an heterogeneous list. You could pack it as existentials, but then you'd lose type information. You could pack it in an untyped list, but that's of course not type-safe. How did you solve this?Mauricio Schefferhttps://www.blogger.com/profile/15247972578064164206noreply@blogger.com