[erlang-questions] illegal guard expression: filelib:is_dir(H)

Richard O'Keefe ok@REDACTED
Mon Feb 15 03:18:50 CET 2010

On Feb 14, 2010, at 11:51 PM, Kay Kay wrote:

> I have a function along the following lines
> do_process_elements([],  _) ->   [];
> do_process_elements([H | _], Fn) ->
>  if
>    filelib:is_dir(H) ->
>      process_directory(H, Fn);
>    true ->
>      process_file(H, Fn)
>  end.
> When I compile - I get the following error,  where the line number  
> refers to    - filelib:is_dir(H) function.
> ./test.erl:23: illegal guard expression
> Given that filelib:is_dir(H) returns true / false  - I thought this  
> would fit in here as a predicate. What could be going wrong ?

Guards are NOT expressions.  They are in a special mini-language
that allows only a small fixed set of guard functions and tests.
Amongst other things, guards are not allowed to have side effects.
This is explained in Erlang books and the Erlang reference manual.

In this case, the answer is simple:

	do_process_elements([H|T], Fn) ->
	    case filelib:is_dir(H)
	      of true  -> process_directory(H, Fn)
                ; false -> process_file(H, Fn)

We had a thread about this last year.  One point in that thread
was that very often you think of something as having two
outcomes and map it to a Boolean, but *really* there are more
outcomes, so you should be using a 'case' anyway.

This is a perfect example of that.
There are some issues with filelib.
For example, file_lib:is_file(F) is true when F is a file or a
directory.  If that hasn't led to mistakes, I'll be surprised.
If you look for a "file", there are at least four outcomes:

	- there is a file system binding for that name
	  and it is a directory
	- there is a file system binding for that name
	  and it is a regular file
	- there is a file system binding for that name
	  and it is something else (character special,
	  block special, symbolic link, named pipe, door, ...)
	  [PS: can anyone explain to me, as to a very small
	  child, what a Solaris "door" is?]
	- there is no file system binding for that name

and of course with symbolic links there is the issue of
whether you want to see them or not.  For the sake of argument,
one simple interface might be

	filelib2:file_type(FileName) ->

You should probably be using something like

	do_process_elements([H|T], Fn) ->
	    case filelib2:file_type(H)
	      of directory -> process_directory(H, Fn)
                ; regular   -> process_file(H, Fn)
                ; other     -> process_other(H, Fn)
                ; missing   -> process_missing(H, Fn)

I have come to regard attempts to use 'if' with user-defined tests
as a "code smell".  They're not always misguided, but they indicate
an underlying problem often enough that I am now _glad_ that 'if'
doesn't allow them.

More information about the erlang-questions mailing list