Sunday, March 27, 2016

dets:select_delete: exception error: bad argument

When first learning about how to delete records from an Erlang dets table, I kept getting this error from the dets:select_delete() function:

Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V7.2.1  (abort with ^G)
1> c(dets_select_delete).  
{ok,dets_select_delete}
2> dets_select_delete:test().
** exception error: bad argument
     in function  dets:select_delete/2
        called as dets:select_delete(test,[{{'_','_','$1'},[{'<','$1',3}],[]}])
3>

Here is the code:

-module(dets_select_delete).
-export([test/0]).

% Test syntax of match clause.
test() ->
    %delete_dets(),
    {ok, test} = dets:open_file(test, [{type, set}, {file, "test.dets"}]),
    % Add some data
    ok = dets:insert(test, {a, 1}),
    ok = dets:insert(test, {b, 2}),
    ok = dets:insert(test, {c, 3}),
    Match = [{{'_','_','$1'},[{'<','$1',3}],[]}],
    DeletedN = dets:select_delete(test, Match),
    DeletedN.

The issue was with the return value clause:

    Match = [{{'_','_','$1'},[{'<','$1',3}],[]}],
                                           ^^^^
                                             |
                                             +-- wrong return value clause.

This is the correct match specification:

    Match = [{{'_','_','$1'},[{'<','$1',3}],[true]}],
                                           ^^^^^^^
                                             |
                                             +-- right return value clause.

I was confused by the examples "Match specifications in Erlang" [http://erlang.org/doc/apps/erts/match_spec.html], the simplest of which always have an empty return clause. Since the match spec is kind of complex, I thought it was a problem in my match spec.

I sorted this out by using the shell, and verifying my match spec was correct with the dets:select/2. Once I knew it was correct, I went back and read the docs to select_delete again, and there it is, clear as day:

Deletes each object from the table Name such that applying the match specification MatchSpec to the object returns the value true.

select_delete(Name, MatchSpec) -> N | {error, Reason}, Retrieved March 27, 2016.

Saturday, February 6, 2016

Erlang: The equals sign is a matching operator

In all previous programming languages I have used before Erlang, the equals sign means assignment. When you see
X = 1;
this reads as "assign the value 1 (one) to X."

Erlang is different. In Erlang, you read this like a mathematical statement: "find the value of x that makes the left-hand side of the equation equal to the right-hand side."

This simple difference in the meaning of = quickly gets more useful. For example, consider this:

"/" ++ Path = HttpRequest:get(path).

In Erlang, this says "find the value of Path that makes this statement true". This is nearly equivalent to the following Java code:

String Path = HttpRequest.getPathInfo().substring[1:];

I say nearly equivalent, because there is something else going on in the Erlang statement. The Erlang equals sign is stating that the equality holds, so a more precise Java translation is:

String Path;
if (HttpRequest.getPathInfo().startsWith("/")) {
   Path = HttpRequest.getPathInfo().substring[1:];
} else {
   String emsg = "no match of right hand side value \"%s \"";
   throw new IllegalStateException(emsg.format(HttpRequest.getPathInfo());
}

That one line of Erlang both asserts a pre-condition and binds a variable to a value.

This technique works in function arguments:

$ cat t.erl
-module(t).
-export([test/1]).

test("/" ++ X) -> X.
$ erl
1> c(t).
{ok,t}
2> t:test("abc").
** exception error: no function clause matching t:test("abc") (t.erl, line 4)
3> t:test("/abc").
"abc"
4>

And for other types:

4> [{X,Y}] = [{1,2}, {2,3}].
** exception error: no match of right hand side value [{1,2},{2,3}]
5> [{X,Y}] = [{1,2}].       
[{1,2}]
6> 

What I am finding as I code more in Erlang is that I now code only the happy path. This is a fun way to write code! I like to write safe code, but having to catch and handle all the error cases sucks the fun out of it. With Erlang, I get both: I write the happy path and I get pre-condition assertions built-in.

And going back to the first example, what are you going to do in a Java Servlet running in a container so buggy that the CGI PATH_INFO variable is missing it's leading slash? Crashing and then restarting the Servlet seems like a reasonable action. That's exactly what Erlang does by combining pattern matching and Supervisor behaviors.