2

Some other advanced languages like Haskell and Perl 6 provide syntactic sugar that lets exceptions be thrown, even in a place where the syntax demands an object. It is acts as though it becomes a thrown exception when the value is used (which would be immediately, in the following very contrived example):

enum BuildMode { Debug, MemoryProfiling, Release };

bool IsDebugMode(BuildMode mode)
{
    return mode == BuildMode.Debug ? true
        : mode == BuildMode.MemoryProfiling ? true
        : mode == BuildMode.Release ? false
        : ThrowException<bool>("Unhandled mode: " + mode);
}

The above helper lets an exception be thrown from a place where a value is allowed but not a statement. I can write this function as follows, though it's not as cool as the Haskell or Perl 6 code, since there's no lazy evaluation:

T ThrowException<T>(string message)
{
#line hidden
    throw new Exception(message);
#line default
}

Is there any canonical way to do this, or any good reason not to?

Edit:

I actually didn't try using throw new Exception() as a value in C# 7 until after posting this. That is the answer, more or less. I'll leave this up, in case future people search for what is the C# equivalent to Perl 6's Failure class or Haskell's error.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
piojo
  • 6,351
  • 1
  • 26
  • 36
  • I am afraid there is no way to do this in c# but I'd be happy proven wrong – Matěj Štágl Aug 10 '18 at 08:27
  • 1
    @MatějŠtágl wellll .... throw expressions – Panagiotis Kanavos Aug 10 '18 at 08:28
  • 1
    @PanagiotisKanavos if throw expressions are the answer (and they could be), then I totally misunderstood when OP wanted them to throw; I thought they wanted them to be *deferred* throws – Marc Gravell Aug 10 '18 at 08:30
  • @MarcGravell: I understood his question as *"Is there a canonical replacment for my (non-lazy) helper method? Oh, and as an aside, Haskell also allows for lazy evaluation of exceptions, can we do that too?"*. – Heinzi Aug 10 '18 at 08:32
  • @MarcGravell I'm still trying to convert the question to familiar F# and C# terms. Plus, the code doesn't show any attempt at lazy evaluation. It would have to return a function instead of an actual value – Panagiotis Kanavos Aug 10 '18 at 08:35
  • If you're thinking of Haskells `error` function, it isn't really an exception in the C# sense. It's more of a regular function with the peculiar side effect of aborting evaluation and printing a message, and it's not syntactic sugar. If that's not what you're thinking of, please explain what you mean. – molbdnilo Aug 10 '18 at 08:39
  • Throw expressions are the answer. I added something to the end of the question to explain that. I know laziness isn't something I can reasonably have in C#. – piojo Aug 10 '18 at 08:42
  • @piojo Lazines is also available through the Lazy class. F# has lazy computations which are essentially a shortcut to generating the Lazy. Or you can return a function instead of a Lazy – Panagiotis Kanavos Aug 10 '18 at 08:53

3 Answers3

6

C# 7.0 supports throw expressions:

return mode == BuildMode.Debug ? true
    : mode == BuildMode.MemoryProfiling ? true
    : mode == BuildMode.Release ? false
    : throw new Exception("Unhandled mode: " + mode);

There is no lazy evaluation, but you don't need your helper method anymore.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
3

I suspect you are looking for throw expressions which were added in C# 7.

return mode == BuildMode.Debug ? true
    : mode == BuildMode.MemoryProfiling ? true
    : mode == BuildMode.Release ? false
    : throw new Exception(...);

One of the most common usages is for null argument validation

var nameValue = value ?? throw new ArgumentNullException(paramName: nameof(value), message: "New name must not be null");

Lazy evaluation

For lazy evaluation you'd have to either return a function or a Lazy :

Lazy<bool> IsDebugMode(BuildMode mode)
{
    bool isDebug() 
    {
        return mode == BuildMode.Debug ? true
            : mode == BuildMode.MemoryProfiling ? true
            : mode == BuildMode.Release ? false
            : throw new Exception(...);
   }

    return new Lazy<bool>(isDebug);
}

And use it as :

var isDbg=IsDebugMode(someMode);
.....
.....
//Will throw here!
if (isDbg.Value)
{
    ...
}

F# provides lazy computations which also return a Lazy with a more convenient syntax :

let isDebugMode mode = 
    match mode with
    | BuildMode.Debug -> true
    | BuildMode.Release -> false
    | _ -> failwith "Ouch!"

let isDbg = lazy (isDebugMode someMode)
...
//Can throw here
if (isDbg.Force() then 
   ...

The same lazy evaluation, using a Func :

Func<bool> IsDebugMode(BuildMode mode)
{
    bool isDebug() 
    {
        return mode == BuildMode.Debug ? true
            : mode == BuildMode.MemoryProfiling ? true
            : mode == BuildMode.Release ? false
            : throw new Exception(...);
   }

    return isDebug;
}

Used as a function :

var isDbg=IsDebugMode(someMode);
...
//Can throw here
if(isDbg())
{
    ...
}

Switch expression

C# 8 will add switch expressions which will probably look like this :

return mode switch {
    BuildMode.Debug           => true,
    BuildMode.MemoryProfiling => true,
    BuildMode.Release         => false,
    _ => throw new Exception (...)
};

The lazy function could look like this :

Lazy<bool> IsDebugMode(BuildMode mode)
{
    bool isDebug() =>
        mode switch {
            BuildMode.Debug           => true,
            BuildMode.MemoryProfiling => true,
            BuildMode.Release         => false,
            _ => throw new Exception (...)
    };

   return new Lazy<bool>(isDebug);
}

Looks a bit more like F#

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
0

The given answers are correct, but I'll add an answer (to my own question) to point out an ideal way to emulate throw expressions in C# 6 and below. It's useful for forward compatibility to have the same name and a similar API, so this is the helper class I settled on:

public class ThrowExpression<T>
{
    public ThrowExpression(string message)
    {
#line hidden
        throw new Exception(message);
#line default
    }

    // never used, but makes the compiler happy:
    public static implicit operator T(ThrowExpression<T> obj)
    {
        return default(T);
    }
}

It's also possible to make a lazy throw expression, which will only throw when it is cast to the target value type. Use your judgment as far as whether to use this class in a way that makes the code less type safe (casting from object to the target type).

public class ThrowExpression<T>
{
    private string message;
    public ThrowExpression(string message)
    {
        this.message = message;
    }

    public static implicit operator T(ThrowExpression<T> obj)
    {
#line hidden
        throw new Exception(message);
#line default
    }
}

Various embellishments are possible, such as accepting different exception types as a parameter or via an additional template argument, but I intend to keep it simple until these enhancements are needed.

piojo
  • 6,351
  • 1
  • 26
  • 36