// C# version of another kind of common error (null pointer) using System; using System.Collections.Generic; public class list { public int head; public list tail; public list(int h, list t) {head=h; tail=t;} } public class error2 { static list cons(int h, list t) { return new list(h,t); } static int car(list t) => t.head; static list cdr(list t) => t.tail; static int lastint(list m) { while (cdr(m)!=null) m=cdr(m); return car(m); } static int length(list m) { int cx=0; for(;m!=null;m=cdr(m),cx++); return cx; } // add value to end of list (destructively) -- WITH INTENSIONAL ERROR static void add(list m, int x) { int len = length(m); while (0 < len--) m=cdr(m); m.tail = cons(x,null); // set tail to point to new cell // where is the error? }//add public static void Main() { list M = cons(2,cons(4,cons(6,null))); list N = null; //Console.WriteLine( lastint(M) ); monadtest(); }//main ///// Testing the MONAD BELOW: //public static bool nullable() => // doesn't work // Nullable.GetUnderlyingType(typeof(T))==null; public static Result unit(T x) // never wrap null in a ValueResult { if (ValueResult.nullallowed || x!=null) return new ValueResult(x); else return new ErrorResult("null pointer"); } public static Result returnit(U x) => unit(x); // alias // more functions for convenience: public static Result Value(U x) => new ValueResult(x); public static Result Error(String x) => new ErrorResult(x); // the last value function, redone with monad public static Result lastval(list m) { var M = unit(m);// put inside mondad while (!(M.fmap(cdr).isError())) M = M.fmap(cdr); return M.fmap(car); } // adding something to the end of the list, WITH SAME MISTAKE public static Result wrongadd(list m, int x) { int len = length(m); var M = unit(m); while (0 < len--) M=M.fmap(cdr); // won't crash! return M.fmap( n => {n.tail=cons(x,null); return n;} ); //m.tail = cons(x,null); // set tail to point to new cell }//add public static Result correctadd(list m, int x) { int len = length(m); var M = unit(m); while (0 < --len) M=M.fmap(cdr); // won't crash! return M.fmap( n => {n.tail=cons(x,null); return n;} ); //m.tail = cons(x,null); // set tail to point to new cell }//add // iterate through list and perform action (f: int->void) public static void forlist(list m, Action f) { var M = unit(m); while (!M.isError()) { M.fmap(car).fmap( x=> { f(x); return 0;}); M=M.fmap(cdr); } } public static void monadtest() ////////***** monadtest { var M = cons(2,cons(4,cons(6,null))); list Nil = null; var RM = unit(M); // type inferred as Result Result RNil = unit(Nil); // to get an ErrorResult object instead of null-pointer exception: //car(Nil); // get null-pointer exception Result result1 = RM.fmap(car); Console.WriteLine(result1); result1 = RNil.fmap(car); Console.WriteLine(result1); result1 = RM.fmap(cdr).fmap(cdr).fmap(car); Console.WriteLine("third value: "+result1); Console.WriteLine( RM.fmap(cdr).fmap(cdr).fmap(cdr).fmap(car) ); // no 4th Console.WriteLine( lastval(M) ); // wrongadd var M2 = wrongadd(M,8); // actually constructive, need to reflect... // still not a compiler error! if (!M2.isError()) { // not required by compiler forlist(M, x=>Console.Write(x+" ")); Console.WriteLine(" -- end of list"); } M2 = correctadd(M,8); // actually constructive, need to reflect... if (!M2.isError()) { // not required by compiler forlist(M, x=>Console.Write(x+" ")); Console.WriteLine(" -- end of list"); } }//monadtest }//error2 main class ///// Build a MONAD Called Result (oop style) public interface Result { bool isError(); // is result a valid value or an error Result fmap( Func f); // U is a new generic var in this fun Result bind(Func> bf); } //static Result unit(T x) class ValueResult : Result { public T val; // the value wrapped inside the monad, and must stay wrapped public static bool nullallowed=false; public ValueResult(T x) { val =x; //Console.WriteLine("val set to "+x); } public bool isError() { return false; } public Result fmap(Func f) { return this.bind( x=>returnit(f(x)) ); } public Result bind(Func> bf) { if (nullallowed || val!=null) return bf(val); else { return new ErrorResult("Invalid value "+val); } // if (!nullable() || !EqualityComparer.Default.Equals(val,default(T))) }//bind public override String ToString() => "Value "+val; public static Result returnit(U x) => error2.unit(x); // alias }// ValueResult class ErrorResult: Result { public String msg; // error message associated with error public ErrorResult(String m) { msg=m; Console.Error.WriteLine("Error Log: "+msg); } public override String ToString() => "Error: "+msg; public bool isError() => true; public Result fmap(Func f) => new ErrorResult("Propagated "+msg); public Result bind(Func> bf) { String newmsg = "Propagated "+msg; Console.WriteLine("Bind Error: "+newmsg); return new ErrorResult("Propagated "+msg); } }//ErrorResult