Don't return null; use a tail call
joelneely.wordpress.com
joelneely.wordpress.com
From an [expression problem](http://c2.com/cgi/wiki?ExpressionProblem) point of view using a discriminated union (in the same spirit as returning null but safer, since you can you peek at the value after doing a null check) would probably be just fine anyway. You still get to avoid the danger of NULL pointers but you still get a real value to work with (unlike the callback case that allways immediately splits it) so you can store it, pass it around, etc.
By the way, astute readers might have recognized all that callback passing as just the [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern) in disguise.
Not sure if serious
Also a 'tail call' and 'tail call elimination' are 2 completely different things. The former being an implementation detail (to provide proper tail recursion for languages that require it, ie Scheme), the latter an optimization technique (to remove (expensive) tail calls!).
If "tail call recursion" means recursing as the last thing you do before returning, then he is saying to send a message as the last thing you do.
AKA send a message as a tail call instead of returning the value of that message.
Indeed. For those seeking more detailed information about this issue, see:
http://c2.com/cgi/wiki?ProperTailRecursion
In the example referenced, the following call stack could be created:
0. authenticateUser
1. login
2. authSucceeded
If authSucceeded continues to call other functions as if it were the new main flow of control you could end up with a very deep call stack (it's not uncommon to see software with a call stack 40 calls deep). This is why I would have preferred "authSucceeded" to be renamed as "registerUserSession" or something else that screams "this function will do a well defined bit of work and _return quickly_" rather than "this is a loosely defined event callback that may do anything". Nullable<int> someNumber = foo.TryAndGetSomeNumber();
if(someNumber && someNumber.HasValue)
doSomething( someNumber.Value );You still have to remember to do this check. Will the compiler complain if you use someNumber.Value without checking that its valid first? If not, then its not really the same thing.
The point of Haskell's Maybe is that the compiler will make sure that you take both cases (that there is a value and that there isn't a value) into account (because to extract the value you must pattern match and pattern matches must be exhaustive, otherwise you get a compile error).
int result;
if (foo.TryAndGetSomeNumber(out result))
doSomething(result);
There might also be a foo.GetSomeNumber() that returns an int or throws on failure.[fixed formatting]
re: haskell: Unfortunately or otherwise, due to influx of devs,the nomenclature has gotten mangled re Maybe/Either and all the other things that (data) can do:
- ADT used to denote abstract data types,
- Algebraic data types used to signify sum (e.g. Maybe and Either) and product (e.g. record) types, I think many people use it for only sum types now (hard to quantify my impressions of current usage, tho)
Here's a good little tutorial: http://www.codecommit.com/blog/scala/the-option-pattern
do_something(N) ->
Pid = self(),
spawn(fun() -> find_x(Pid, N) end),
receive
{found, Item} -> ...;
not_found -> ...
end.
find_x(Pid, N) -> find_x (Pid, N, [1, 3, 5]).
find_x(Pid, _N, []) -> Pid ! not_found;
find_x(Pid, N, [N|_T]) -> Pid ! {found, N};
find_x(Pid, N, [_H|T]) -> find_x(Pid, N, T).
You can also handle timeouts and stuff. find_x(N) -> find_x (N, [1, 3, 5]).
find_x(N, []) -> {error, not_found};
find_x(N, [N|_T]) -> {ok, N};
find_x(N, [_H|T]) -> find_x(N, T).
If what you want is to get out of deep recursion and the above doesn't work especially well, that's what 'throw/1' is for: Res = try find_x(N) of
Val -> {ok, Val}
catch
throw:not_found -> {error, not_found}
end.
This will allow to avoid the stack, special cases for returning values and give you the code result you need.The spawning of a process for a simple use case like this is only worth it if you need a timer to give a maximum execution time for a given request. Otherwise, it's not very useful, clear or efficient. I'd mark it as 'concurrency for the sake of it'.
The above link has some very interesting links about OO design and SmallTalk right at the end.
* Wrong username
* Wrong password
* Login procedure aborted due to external factor
* Programming error
This causes a headache, since the client might forget to check for the magic value 'null' and the symptom might therefore accidentally be deferred to any later time.Instead of using a callback method, why not just return an instance of some more sophisticated class, i.e something like this:
public AuthenticationResult authenticateUser(User user) {
...
}
with a possible usage: AuthenticationResult authResult = authenticateUser(user);
if (authResult.failed()) {
notifyUser(authResult.getFailureReason());
}
If the client then tries to use a failed AuthenticationResult as a successful one, or use a successful AuthenticationResult as a failed one, the programmer is free to throw any informative exception. Example: class AuthenticationResult {
...
public FailureReason getFailureReason() {
if (!failed) {
throw new IllegalStateException("you cannot fetch a failure reason from a sucessful AuthenticationResult!");
}
}
public AuthToken getAuthToken() {
if (failed) {
throw new IllegalStateException("you cannot fetch a auth token from a failed AuthenticationResult!");
}
}
}
If one instead would use 'null' as a magic value for 'auth failed', the dreaded NPE would show up instead!See, the calling code probably can't correctly proceed unless authentication was successful, and there's no chance that the caller can simply forget to handle an exception -- at the very least, it'll hit a top-level exception handler and/or crash the program.
Also, let me clarify that I don't believe that exceptions should be used as the only way to return values from a method in languages that support them. They are simply a tool with certain properties (among them, the ability to be more difficult to ignore, the topic at hand).
slides: lets make a blocking API not block. wait, what? (a practical introduction to continuations) [https://docs.google.com/present/view?id=dv9r8sv_82cn7dngcq]
Isn't this pretty much what you do in Twisted already?
from another pov: this article describes how the designers of twisted want you to think (ie continuation passing style), but the actual implementation is slightly different.
http://en.wikipedia.org/wiki/Trampoline_(computing)#High_Lev...
As to which is better, type-checked null is referentially transparent, which is a nice objective measurement of complexity, and referentially transparent functions are generally more reusable than those that aren't. It also keeps my mental model of the stack tidy, tail-call optimization or no.
QED, right? have I missed anything?
One thing that the callback style enables is that you can have more than just success and failure and/or you can provide more information about why it failed.
I don't particularly like this callback style though. I do like the multi return value style in languages like Go* where, by convention, an error is returned as the last parameter. You still have to check whether the error itself was nil, but at least you get a reason why; It is also part of the method signature, meaning you can ignore it, but you have to explicitly ignore it.
While that doesn't make it referentially transparent, it does force the programmer to acknowledge the possibility of error, even if they don't deal with it. It also keeps the flow more sequential and often times easier to read than the callback style.
*C#, Ruby and others also have some form of multiple-return values, but it's rare to see them used in this way.