Drago-QCC
Drago-QCC
Explore posts from servers
CC#
Created by Drago-QCC on 8/15/2024 in #help
How can I make this library I released better / general thoughts on Result and Unions?
Intro So a few days ago I released a library called UnionContainers, I've been working on it for a while along with some other libraries and I've also recently gotten some feedback from people over on the dotnet and C# subreddits. I wont go too in depth into the library as I'm not trying to showcase it but want to ask everyone's thoughts on what can be done to make it more user friendly. While I did not get a ton of useful comments, most either just dismissing the idea or not offering any explanations into the down votes I did get a few here or there that pointed out a few key things.
1. The general consensus seemed to be the library API was too complex or hard to follow naturally 2. People seemed to not like that I merged a Result<T> and a Option<T> types into 1 class 3. Some pointed out issues about non exhaustive matching (the library does enforce it with Roslyn Analyzers but that's probably my fault for not being more direct about that info) So with that said I would like to show the current implementation, share some thoughts on why I created it and what benefits I see from it, and then propose a more simplified implementation. The What So as a hopefully quick summary, the UnionContainer type currently supports 4 main states that it can exist in, those are Empty, Result, Error and Exception. It also can support container creation for between 1 and 16 possible result types. A very basic example would be this
c#
UnionContainer<string> container = "hello";
container.TryHandleResult((String result) => Console.WriteLine($"The container value is: {result}"));
//prints The container value is: hello
c#
UnionContainer<string> container = "hello";
container.TryHandleResult((String result) => Console.WriteLine($"The container value is: {result}"));
//prints The container value is: hello
This on its own does not offer a done of useful functionality since we can obv tell the container has a result given we set it as a string directly. Another example that is a bit more complicated could be something like this
c#
public async Task<HttpResponseMessage> GetHTTPResponseUnion()
{
var client = new HttpClient();
UnionContainer<HttpResponseMessage> container = await UnionContainerFactory.MethodToContainer(async () => await client.GetAsync("https://127.0.0.1"));
return container.TryGetValue(fallbackValue: new HttpResponseMessage())!;
}
c#
public async Task<HttpResponseMessage> GetHTTPResponseUnion()
{
var client = new HttpClient();
UnionContainer<HttpResponseMessage> container = await UnionContainerFactory.MethodToContainer(async () => await client.GetAsync("https://127.0.0.1"));
return container.TryGetValue(fallbackValue: new HttpResponseMessage())!;
}
Here we see the method creates an HttpClient executes a get request and then returns the containers result as the return value of the task. This introduces a few important features. The first being the automatic wrapping of a method in a try-catch, when client.GetAsync fails to connect it will throw an exception, the MethodToContainer call will see that and save the Exception so it can retrieved later. If the result works however the exception state stays false and instead the result item is populated. An optional value can also provided to TryGetValue that it will return back if your result item isn't present or is null.
A more complete example of using union container and matching it can be seen in this GitHub Gist https://gist.github.com/DragoQCC/28cbf2e9f1c7d2493ac91c64905bcec3 (had to post here due to post character size limit) The Why I wanted a result type class that could allow me to return one of a multitude of values, and also keep track of issue states as they arose all at the same time. I did not want to set result types for Empty, Error, and Exception along side the valid result types that I actually want to get back. The Proposal My current thoughts are to simplify the language used like TryHandleResult becomes Match, TryGetResult becomes GetResult, and remove the error type. Closing Thoughts The library contains some other features like Attributes that let you constrain generics, dynamic / object types for class generics, method arguments, properties, and fields to an allowed list of types as well as some functional extension methods, but I wanted to focus on the main Union / Result type content. Interested to hear your thoughts and any advice / pointers on what can be done to help improve the library.
3 replies
MCMetalama Community
Created by Drago-QCC on 9/30/2023 in #technical-questions
Giving Aspects Metadata ?
I've only just started messing with MetaLama and going thru the documentation and videos but I was wondering if its possible to give an attribute extra metadata to send based on the place its invoked for example
public class HookFunctionAndPrint : OverrideMethodAspect
{
public override dynamic? OverrideMethod()
{
//hooks on method start
var functionName = meta.Target.Method.Name;
Console.WriteLine($"Hooked on {functionName} method start");
try
{
// Let the method do its own thing.
return meta.Proceed();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
return null;
}
finally
{
Console.WriteLine($"Hooked on {functionName} method end");
}
}
}
public class HookFunctionAndPrint : OverrideMethodAspect
{
public override dynamic? OverrideMethod()
{
//hooks on method start
var functionName = meta.Target.Method.Name;
Console.WriteLine($"Hooked on {functionName} method start");
try
{
// Let the method do its own thing.
return meta.Proceed();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
return null;
}
finally
{
Console.WriteLine($"Hooked on {functionName} method end");
}
}
}
but what if I wanted extra metadata such as stuff you get along with MEF plugins if you are familiar with that but something lets say a string to set a logging verbosity.
public class HookFunctionAndPrint : OverrideMethodAspect
{
public override dynamic? OverrideMethod()
{
//hooks on method start
var functionName = meta.Target.Method.Name;
Console.WriteLine($"Hooked on {functionName} method start");
try
{
// logging level here would be a defined name when the attribute is set
if(meta.Metadata.loggingLevel == "Verbose")
{
Console.WriteLine($"verbose logging enabled for {functionName}");
}
else if(meta.Metadata.loggingLevel == "info")
{
Console.WriteLine($"info only logging enabled for {functionName}");
}
// Let the method do its own thing.
return meta.Proceed();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
return null;
}
finally
{
Console.WriteLine($"Hooked on {functionName} method end");
}
}
}
public class HookFunctionAndPrint : OverrideMethodAspect
{
public override dynamic? OverrideMethod()
{
//hooks on method start
var functionName = meta.Target.Method.Name;
Console.WriteLine($"Hooked on {functionName} method start");
try
{
// logging level here would be a defined name when the attribute is set
if(meta.Metadata.loggingLevel == "Verbose")
{
Console.WriteLine($"verbose logging enabled for {functionName}");
}
else if(meta.Metadata.loggingLevel == "info")
{
Console.WriteLine($"info only logging enabled for {functionName}");
}
// Let the method do its own thing.
return meta.Proceed();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
return null;
}
finally
{
Console.WriteLine($"Hooked on {functionName} method end");
}
}
}
then when you give the attribute to the function you do something like
[HookFunctionAndPrint]
[MetaLamaMetadata("Logging", "Verbose")]
static void Main(string[] args)
{
}
[HookFunctionAndPrint]
[MetaLamaMetadata("Logging", "Verbose")]
static void Main(string[] args)
{
}
I dont know if something like this alrady exists and I just missed it in the docs or if this would be a feature request. Also currently on the free license if this is a premium feature 🙂
6 replies