c# - Functional Principles in objectorientated Programs -


today saw piece of code , thinking it's sense. made little example can see how works. far know should nested functionality heavy use of lambda-caculus used in functional languages. because find not understandable (jumping file file) know if of have similiar experience this.

example function one

    public void dosomethinga(int inputint, action<int?, exception> result)     {         try         {             int? retval = inputint;             result(retval, null);         }         catch (exception e)         {             result(null, e);         }     } 

example function two

    static void dosomethingb(int? baseint, action<exception> result)     {         try         {             result(null);         }         catch (exception e)         {             result(e);         }     } 

example call of both.

    public int? calculatesomething(int input)     {         int? result;          dosomethinga(input, (resultint, aexception) =>         {             if (aexception != null)             {                 return;             }              dosomethingb(resultint, bexception =>             {                 if (bexception != null)                 {                     return;                 }                  result = resultint;             });         });         return null;     } 

it's maybe interesting normale "main" function have sort of promise. registered functions , waits end , get's result.

what you're describing monad. in particular try monad. i'll show below how implement try monad, , code calculatesomething this:

public try<int> calculatesomething(int input) =>     x in dosomethinga(input)     y in dosomethingb(x)     select y; 

which measure pretty easy understand.

first declare delegate represent try operation:

public delegate tryresult<t> try<t>(); 

next define tryresult<t>. capture return value on-success, , failure exception on-fail:

public struct tryresult<t> {     internal readonly t value;     internal readonly exception exception;      public tryresult(t value)     {         value = value;         exception = null;     }      public tryresult(exception e)     {         exception = e;         value = default(t);     }      public static implicit operator tryresult<t>(t value) =>         new tryresult<t>(value);      internal bool isfaulted => exception != null;      public override string tostring() =>         isfaulted             ? exception.tostring()             : value.tostring();      public static readonly tryresult<t> bottom = new invalidoperationexception(); } 

next define extension methods try delegate (this little known feature of c# delegates support extension methods):

public static class tryextensions {     public static tryresult<t> try<t>(this try<t> self)     {         try         {             if (self == null) return tryresult<t>.bottom;             return self();         }         catch (exception e)         {             return new tryresult<t>(e);         }     }      public static r match<t, r>(this try<t> self, func<t, r> succ, func<exception, r> fail)     {         var res = self.try();         return res.isfaulted             ? fail(res.exception)             : succ(res.value);     } } 

they both allow invoking of try delegate in safe way. key match extension method, 'pattern matches' on result:

int res = calculatesomething(1).match(               succ: value => value,               fail: excep => 0                ); 

so you're forced acknowledge function throw exception @ value.

one thing that's missing here how works linq:

public static class tryextensions {     public static try<u> select<t, u>(this try<t> self, func<t, u> select) =>         new try<u>(() =>         {             var rest = self.try();             if (rest.isfaulted) return new tryresult<u>(rest.exception);             return select(rest.value);         });      public static try<v> selectmany<t, u, v>(         try<t> self,         func<t, try<u>> bind,         func<t, u, v> project ) =>             new try<v>(() =>             {                 var rest = self.try();                 if (rest.isfaulted) return new tryresult<v>(rest.exception);                  var resu = bind(rest.value).try();                 if (resu.isfaulted) return new tryresult<v>(rest.exception);                  return new tryresult<v>(project(rest.value, resu.value));             }); } 

select allows work:

var res = x in y           select x; 

selectmany allows work:

var res = x in y           z in x           select z; 

that allows multiple statements run in sequence. known monadic bind (but don't need know work - i'd rather not write monad tutorial here). capturing nesting pattern in calculatesomething example, never have manually write again.

and that's it. code above, write once , once only. let's implement dosomething functions:

public try<int> dosomethinga(int inputint) => () =>    inputint;  public try<int> dosomethingb(int inputint) => () => {     throw new exception(); }; 

note how they're defined lambdas. if you're using c# 5 or lower this:

public try<int> dosomethinga(int inputint) {     return () => inputint; } 

if want see fuller implementation of this, check out language-ext functional library c# there's try implementation there. has many more useful extension methods allow write functional code without bolierplate of try/catch.


Comments

Popular posts from this blog

java - Jasper subreport showing only one entry from the JSON data source when embedded in the Title band -

mapreduce - Resource manager does not transit to active state from standby -

serialization - Convert Any type in scala to Array[Byte] and back -