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
Post a Comment