Wednesday, 7 October 2009

evaluating exception handling

Are your custom exceptions necessary?

Are you deriving from ApplicationException? Deriving from Exception is fine.

Are your exceptions grouped in an exception namespace?

Do your exceptions use localized description strings?

Are you including extra or sensitive information unwisely or unnecessarily?

Are you using exceptions to handle normal code flow? You shouldn’t.

Are you using a tester-doer pattern to help people avoid common exceptions? File.Exists and File.Open for example… Strictly not a great example, but see this msdn guide to exception handling.

Do you return null from methods that would normally return a resource handle of some sort – or do you throw?

Does normal running throw an exception? It shouldn’t.

Are you checking method arguments and throwing ArgumentExceptions? You should.

Is all your cleanup code in finally blocks?

Do your custom exceptions present at least the three common constructors?

Are you catching Exception? Don’t – or at most only once per thread.

Are you using throw ex? Don’t. Just throw and avoid clearing the stack trace.

Are you logging Exception.ToString() in preference to Exception.Message?

What do you do if your finally cleanup fails?

Are your custom exceptions [Serializable]?

 

MSDN Design Guidelines for Exceptions

Thursday, 1 October 2009

currying

2> Adder = fun(X,Y) -> X+Y end.
#Fun<erl_eval.12.113037538>

3> AddTwo = fun(X) -> Adder(2,X) end.
#Fun<erl_eval.6.13229925>

4> Adder(3,4).
7

5> AddTwo(5).
7

Yariv's Blog: metacurrying (just for interest's sake)

Wednesday, 30 September 2009

functions and funs

To create a function that takes a fun as an argument

   1: mangle(F, X) ->
   2:     F(X).
4> play:mangle(fun(X) -> (X*X) end, 3).

Remember that if you’re defining the fun inline to add the end still – or you’ll see

* 1: syntax error before: ')'

To pass in a function to something that needs a fun, pass a function reference and remember the arity

   1: double(X) ->
   2:     2*X.
6> play:mangle(fun play:double/1, 3).
6

And finally to create a function that returns a fun

   1: getMangler() ->
   2:     (fun(X) ->
   3:         play:double(X) end).
   4:  
1> Mangler = play:getMangler().
#Fun<play.0.65855982>
2> Mangler(3).
6
3> play:mangle(Mangler, 3).
6

and you can naturally just put the getMangler into the call direct:

4> play:mangle(play:getMangler(), 3).
6

Note that this won’t work:

5> play:mangle(fun play:getMangler/0, 3).
** exception error: play:getMangler/0 called with one argument
     in function  play:mangle/2

because you’re attempting to call getMangler with the argument 3.

Monday, 28 September 2009

tuples and templates

How can we be sure that we produce and consume tuples that are of the form we expect? Do we have to wait to runtime?

process management

Processes hang around, as you might expect. First of all, pman is the Erlang process manager. Start with

1> pman:start().

Hide system processes to make it a bit clearer for now. If we run the simple message passing example from here, then we can see that a play:receiver() process is left hanging around after everything stops.

2> play:start().
in receiver <0.46.0>
rx: hey <0.47.0>
<0.47.0>
in receiver <0.46.0>

To kill the process, highlight it with a click and choose Trace/Kill. pman has a default 5s refresh, so it’ll not disappear immediately…

favour short tasks

Divide your workload into chunks that are small duration compared to your time-for-delivery.

For long tasks, use mechanisms that allow redundant processing to ensure delivery. Ensure that you start your long tasks first.

message passing

   1: -module(play).
   2: -export([headstrip/1,message/1,start/0,receiver/0,sender/1]).
   3:  
   4: sender(RxPid) ->
   5:     RxPid ! {hey, self()}.
   6:  
   7: receiver() ->
   8:     io:format("in receiver ~p~n",[self()]),     
   9:     receive
  10:         {hey, TxPid} -> io:format("rx: ~p ~p~n",[hey, TxPid])
  11:     end.
  12:  
  13: start() ->
  14:     RxPid = spawn(play, receiver, []),
  15:     spawn(play, sender, [RxPid]).
28> play:start().
in receiver
rx: hey
<0.150.0>

receive blocks, and handles one message

If you want your receiver to hang around, call it again. e.g. add a call to receiver() just before end in the code above. But then – whole process finishes. Is something running in the background still? How do I check? [see http://timbar.blogspot.com/2009/09/process-management.html]

remember to export

Remember that every function called must be exported – not just the bootstrap function you use to kick things off. In this context the error

=ERROR REPORT==== 25-Sep-2009::16:51:33 ===
Error in process <0.98.0> with exit value: {undef,[{play,sender,[<0.97.0>]}]}
indicates that you've forgotten to export the function "sender".

unhandlable messages are just dropped

Messages that receive doesn’t know how to handle are just forgotten. How to do a default response? Try sending eef instead of hey in the code above.

Friday, 25 September 2009

difference between foreach and map

map creates a new list from an existing list by applying a function to each existing member.

Use foreach if you want to trigger actions based on list members, not just create new list members.

For example – working from a list of contacts you might want to send a message to everyone whose name matches some string. You’ve no reason to create a new list in this case.

To message an agent – or not – assume we have this function:

   1: -module(play).
   2: -export([message/1]).
   3:  
   4: message({Agent,Location}) when Agent == "John" ->
   5:     io:format("Messaging ~w in ~w~n",[Agent,Location]);
   6: message({Agent,Location}) ->
   7:     io:format("No message for ~w~n",[Agent]).
5> play:message({"John","Buenos Aires"}).
Messaging [74,111,104,110] in [66,117,101,110,111,115,32,65,105,114,101,115]
ok
6> play:message({"Jeff","Buenos Aires"}).
No message for [74,101,102,102]
ok

(Ha, unexpectedly turns strings to lists of ASCII character codes. Need to look into string handling.)

3> lists:foreach(fun play:message/1, [{"John","Buenos Aires"}, {"Jules","Nairobi"}]).
Messaging [74,111,104,110] in [66,117,101,110,111,115,32,65,105,114,101,115]
No message for [74,117,108,101,115]
ok

Surprised by the need to recast our message function explicitly as a fun to pass it to foreach. Need to look into that too. Is it because passing a naked play:message/1 looks like an atom?

function signature dropthrough a bit like case

   1: price_by_age({car,Age}) -> 10 * (10 - Age);
   2: price_by_age({boat,Age}) -> 20 * (20 - Age).

is equivalent to

   1: price_by_age(Vehicle) ->
   2:     case Vehicle of
   3:         {car,Age} -> 10 * (10 - Age);
   4:         {boat,Age} -> 20 * (20 - Age)
   5:     end.

recursive and iterative

   1: sum([H|T]) -> H + sum(T);
   2: sum([]) -> 0.

1 + sum(2,3)
1 + 2 + sum(3)
1 + 2 + 3 + sum()
6
   1: sum(L) -> sum(L,0).
   2:  
   3: sum([H|T],Accumulator) -> sum(T,Accumulator + H);
   4: sum([],Accumulator) -> Accumulator.

sum([2,3], 1)
sum([3], 3)
sum([], 6)
6

Using the accumulator means you can pass state to the next call rather than having to keep all your state hanging around while you progress.

Thursday, 24 September 2009

Installing Windows Services programmatically

After a few problems with using the ManagedInstallerClass (passing arguments in), and noticing that the MSDN docs say it’s not meant to be used in any case, I moved to using this sort of code instead:

 

   1: using System;
   2: using System.Configuration.Install;
   3: using System.ServiceProcess;
   4: using System.Threading;
   5: using System.Diagnostics;
   6: using System.IO;
   7: using System.Reflection;
   8: using System.Collections.Generic;
   9:  
  10: namespace SimpleWindowsServiceManager
  11: {
  12:     class Program
  13:     {
  14:         static void Main(string[] args)
  15:         {
  16:             string fullpath = args[0];
  17:             string[] arguments = args[1].Split(' ');
  18:             string servicename = Path.GetFileNameWithoutExtension(Path.GetFileName(args[0]));
  19:             foreach (string arg in arguments)
  20:             {
  21:                 if (arg.Contains("name"))
  22:                 {
  23:                     string[] bits = arg.Split(new char[] { '=' });
  24:                     servicename = bits[1];
  25:                 }
  26:             }
  27:  
  28:             #region Test to see if it's a service
  29:             try
  30:             {
  31:                 AssemblyInstaller.CheckIfInstallable(fullpath);
  32:             }
  33:             catch (Exception ex)
  34:             {
  35:                 Console.WriteLine("Assembly's not installable");
  36:                 return;
  37:             }
  38:             #endregion
  39:  
  40:             #region install
  41:             Assembly assembly = null;
  42:             try
  43:             {
  44:                 assembly = Assembly.LoadFrom(fullpath); // LoadFrom probes path for dependencies -- LoadFile does not.
  45:             }
  46:             catch (Exception ex)
  47:             {
  48:                 Console.WriteLine("Couldn't load assembly: " + ex.Message);
  49:                 return;
  50:             }
  51:             AssemblyInstaller installer = new AssemblyInstaller(assembly, null);
  52:             Dictionary state = new Dictionary();
  53:             try
  54:             {
  55:                 installer.Install(state);
  56:                 installer.Rollback(state);
  57:             }
  58:             catch (Exception ex)
  59:             {
  60:                 Console.WriteLine("Trouble pre-installing assembly");
  61:                 return;
  62:             }
  63:             #endregion
  64:  
  65:             installer = new AssemblyInstaller(assembly, arguments);
  66:             state = new Dictionary();
  67:  
  68:             #region Install service
  69:             try
  70:             {
  71:                 installer.Install(state);
  72:             }
  73:             catch (InvalidOperationException iex)
  74:             {
  75:                 Console.WriteLine("Install not applicable for this assembly:\n" + iex.Message);
  76:                 return;
  77:             }
  78:             catch (Exception ex)
  79:             {
  80:                 Console.WriteLine(ex.Message);
  81:             }
  82:             #endregion
  83:  
  84:             Console.WriteLine("Getting service controller for " + servicename);
  85:             ServiceController controller = new ServiceController(servicename);
  86:  
  87:             #region Start service
  88:             try
  89:             {
  90:                 Console.WriteLine("Starting service");
  91:                 controller.Start();
  92:             }
  93:             catch (Exception ex)
  94:             {
  95:                 Console.WriteLine(ex.Message);
  96:             }
  97:             #endregion
  98:  
  99:             Thread.Sleep(10000);
 100:  
 101:             #region Stop service
 102:             try
 103:             {
 104:                 Console.WriteLine("Stopping service");
 105:                 controller.Stop();
 106:             }
 107:             catch (Exception ex)
 108:             {
 109:                 Console.WriteLine(ex.Message);
 110:             }
 111:             #endregion
 112:  
 113:             Thread.Sleep(10000);
 114:  
 115:             #region Uninstall service
 116:             try
 117:             {
 118:                 installer.Uninstall(state);
 119:             }
 120:             catch (Exception ex)
 121:             {
 122:                 Console.WriteLine(ex.Message);
 123:             }
 124:             #endregion
 125:         }
 126:     }
 127: }

head strip with guard

   1: -module(play).
   2: -export([headstrip/1]).
   3:  
   4: headstrip([]) -> ok;
   5: headstrip([Head|Rest]) when Head > 4 ->
   6:     io:format("~w bingo!~n", [Head]),
   7:     headstrip(Rest);
   8: headstrip([Head|Rest]) -> 
   9:     io:format("~w~n",[Head]),
  10:     headstrip(Rest).
3> play:headstrip([1,2,3,4,5,6,7,8,9]).
1
2
2
3
4
5 bingo!
6 bingo!
7 bingo!
8 bingo!
9 bingo!
ok

Wednesday, 23 September 2009

recursively strip head element from list

   1:  -module(play).
   2:  -export([headstrip/1]).
   3:   
   4:  headstrip([]) -> ok;
   5:  headstrip([HeadRest]) -> 
   6:      io:format("~w~n",[Head]),
   7:      headstrip(Rest).