7
\$\begingroup\$

So I really want to have something similar to discriminated unions in C#.

One way to do it is to use a visitor pattern, but it takes half a life to write all broilerplate code by hands. There is another way that would allow me writing a bit less code, but it is based on delegates.

Consider the analogue of Options from F#:

public abstract class Optional<TValue>
{
    public abstract TResult Resolve<TResult>(Func<TValue, TResult> someHandler, Func<String, TResult> noneHandler);
}

public class Some<TValue> : Optional<TValue>
{
    public Some(TValue value)
    {
        this.Value = value;
    }
    public TValue Value { get; private set; }
    public override TResult Resolve<TResult>(Func<TValue, TResult> someHandler, Func<String, TResult> noneHandler)
    {
        return someHandler(this.Value);
    }
}

public class None<TValue> : Optional<TValue>
{
    public None(String message)
    {
        this.Message = message;
    }
    public String Message { get; private set; }
    public override TResult Resolve<TResult>(Func<TValue, TResult> someHandler, Func<String, TResult> noneHandler)
    {
        return noneHandler(this.Message);
    }
}

public class Demo {
    public Optional<Uri> ParseAbsoluteUri(String whateverUrl)
    {
        if (Uri.IsWellFormedUriString(whateverUrl, UriKind.Absolute))
        {
            var uri = new Uri(whateverUrl, UriKind.Absolute);
            return new Some<Uri>(uri);
        }
        else
        {
            return new None<Uri>("Given URL is not absolute.");
        }
    }
    public String ProcessUrl(String whateverUrl)
    {
        var uriOpt = this.ParseAbsoluteUri(whateverUrl);
        var result = uriOpt.Resolve(uri => "Hey, your URL \"" + uri + "\" looks nice!", message => "Sorry, the URI that you gave looks bad: " + message);
        return result;
    }
    public void Test()
    {
        Trace.WriteLine(this.ProcessUrl("http://google.com"));
        Trace.WriteLine(this.ProcessUrl("Not url at all"));

        /* output: 
            Hey, your URL "http://google.com/" looks nice!
            Sorry, the URI that you gave looks bad: Given URL is not absolute.
         */
    }
}
  1. Each delegate is essentially a class that the C# compiler makes to wrap the pointer to a function. So with the excessive use of such approach will I hit any sort of problems? (Bloating the size of the assembly for example?)

  2. Delegates aren't the fastest things in the galaxy, especially multicasts ones, this approach uses them heavily.

  3. Making a call to a virtual method lessens (if not eliminates at all) a chance for JIT inlining.

So all in all I am a bit scared to go all the way this way due to performance concerns. I would like to know what you think of this.

\$\endgroup\$
  • 1
    \$\begingroup\$ Options don't have a message, this is more like Choice<T, string>. \$\endgroup\$ – svick Sep 26 '13 at 20:56
  • 1
    \$\begingroup\$ If you have performance concerns, measure your code in realistic situations. Only can answer whether the performance is good enough, because we don't know how exacly would you use this code or what "good enough" is for you. \$\endgroup\$ – svick Sep 26 '13 at 21:00
  • \$\begingroup\$ I am with you about measuring the code. I cannot measure the performance until I write some code and when I measure some code won't give me an overall picture of how it would look like in a real project. With this in mind, basically what you are saying is that I should use it everywhere in a real project and see what happens. Sure thing that would answer the question. Is there anything we can tell about this approach before jumping into it? \$\endgroup\$ – Trident D'Gao Sep 26 '13 at 21:08
  • 3
    \$\begingroup\$ I've been doing this for ~2 years now. ( bugsquash.blogspot.com/2012/01/… ) Unless you're writing super high-performance code, you're fine. It doesn't have any real perf impact in my code. \$\endgroup\$ – Mauricio Scheffer Sep 26 '13 at 22:17
  • \$\begingroup\$ Also, you'll want to make the type closed. Otherwise you can't really check totality. \$\endgroup\$ – Mauricio Scheffer Sep 26 '13 at 22:23
3
\$\begingroup\$

This looks very well crafted. I don't know and I'm not really into functional programming, but I can enumerate everything I like about this code:

  • Type parameter names are descriptive: TValue and TResult are perfect.
  • The interface for Optional<TValue> is segregated; it's very focused and specialized, which makes it highly reusable... which is a nice feature for an abstract class.
  • Usage of this qualifier is consistent.

Well as I enumerated these points I did find a few minor nitpicks:

  • Usage of this qualifier is redundant.
  • Usage of String could be replaced with C# language alias string (but then again usage of String is beautifully consistent).

And as I enumerated these minor nitpicks I did find one tiny little thing that could be improved: the Message property in the derived types is only assigned through the constructor, using the private setter. I'd make it readonly and change the auto-property for a get-only property:

public None(String message)
{
    _message = message;
}

private readonly String _message;
public String Message { get { return _message; } }

...and this is where I'm clashing with this - I prefer prefixing private fields with an underscore and avoid this altogether, you might have it like this instead:

public None(String message)
{
    this.message = message;
}

private readonly String message;
public String Message { get { return this.message; } }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.