11
public static Func<V> To<T, V>(this Func<T> g, Func<T, V> h)
{
    return () => h(g());
}

public static Func<T> ToIdentity<T>(this T t)
{
    return () => t;
}

I sometimes use these and others when delaying evaluation. Are these already in the .net library?

Edit:

Here is an example usage:

public static string SuffixColumn(this string v, string suffix, int columns)
{
    return
    v.ToIdentity()
        .ToScrubbedHtml()
        .ToFormat(() => "{0} " + suffix.ToLower().PadLeft(columns, ' '))
        .ToErrorText(v, suffix, columns)();
}

ToErrorText checks 'v' as legitimate (non-error code, non-null, etc), if good it runs the chained in Func, if bad it produces fail-safe text result. If v, suffix, or columns are not good then ToFormat will never be called. (Hence the delayed/non-evaluated usage).

ToFormat is nearly the composition of the provided Func and string.Format. ToIdentity is used to lift v to a Func and then everything in the chain is based on some Func of T.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
lucidquiet
  • 6,124
  • 7
  • 51
  • 88
  • 1
    how do you delay evaluation using code provided ? – Tigran Jan 23 '12 at 19:48
  • 4
    The short answer is no. I am curious though. These are fairly unusual combinators in that they return methods that take no arguments. What are you using them for? Also, the latter is badly named; an identity function has the property that its input is equal to its output, but the function returned here takes no inputs. – Eric Lippert Jan 23 '12 at 19:54
  • Thanks for the feedback -- I'm not really sure what else to call ToIdentity. And To() should probably called Compose. But each result in a function that doesn't take any arguments, so not exactly the same thing. – lucidquiet Jan 23 '12 at 19:57
  • 2
    `() => t` should still be preferable over `t.ToIdentity()` IMHO. – vgru Jan 23 '12 at 19:59
  • @Jason: but the resulting function accepts no inputs, so I wouldn't call it a map. – vgru Jan 23 '12 at 20:00
  • I've added an example usage. Hopefully doesn't confuse the discussion . – lucidquiet Jan 23 '12 at 20:16
  • @EricLippert: Question's been updated, don't think you'd have got notified. – George Duckett Jan 23 '12 at 21:13
  • All the more so since one would expect identity to be `t => t`. – Jon Hanna Jan 23 '12 at 21:30

2 Answers2

36

Let me see if I understand: you are using lambdas to capture values in a "wrapper" object -- a Func<T> -- and then build a workflow of lazily-evaluated functions around the wrapped object, yes?

enter image description here

Though building it out of functions works, I would personally be inclined to build the monad out of some custom type; it seems too easy to conflate functions that act on the monadic type or its "underlying" type with instances of the monad itself. You are basically building the identity monad using functions; I'd be more inclined to simply create a class or interface with a name that reflects the purpose to which you are putting the identity monad.

You might also consider renaming your methods. Your "To" is traditionally called "Bind" and your "ToIdentity" is traditionally called "Unit".

That is, a monad pattern for a monad type M<T> usually has the methods:

public static M<V> Bind<U, V>(this M<U> m, Func<U, M<V>> k)

and

public static M<T> Unit<T>(this T value)

The bind allows you to implement composition of functions on monads that are analogous to functions that compose on the "underlying" types of the monads.

For a gentle introduction to the marvels of monads in C#, see my erstwhile coworker Wes Dyer's great article on the subject:

https://learn.microsoft.com/en-us/archive/blogs/wesdyer/the-marvels-of-monads

To answer your actual question:

Are these already in the .net library?

Those exact functions are not in the framework. But functions very much like those exist in the framework. For example, SelectMany on IEnumerable<T> is analogous to your To method; it implements the monadic bind operation on sequences. new Nullable<int>(123) is analogous to your "ToIdentity" method; it implements the monadic unit operation for the "maybe monad". And so on.

The CLR type system is insufficiently rich to express the monad pattern in general; for that you need a "higher" type system, like that of Haskell. You'll have to build each specific monad yourself. However, it looks like you are well on your way.

Some more thoughts on using monads in C#-like languages:

Monads in C# -- why Bind implementations require passed function to return a monad?

Monad in plain English? (For the OOP programmer with no FP background)

Why there is no something like IMonad<T> in upcoming .NET 4.0

In C#, What is a monad?

Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
1

You can use Lazy to lazy-load values.

Lazy<int> lazy = new Lazy<int>(() => delayEvaluatingMe());
int delayed = lazy.Value;
Bort
  • 7,398
  • 3
  • 33
  • 48