10

I came accross this code on the web:

is_char(Ch) ->         
    if Ch < 0 -> false;  
       Ch > 255 -> false;
       true -> true      
    end.

is_string(Str) ->            
    case is_list(Str) of           
    false -> false;           
    true -> lists:all(is_char, Str)
    end.

Its is the Guard I alwais dreamed off, in that it checks if an input is a string -- how ever, I'm not allowed to use it in erlang, why is this? And is there a work around?

I would like to be able to write stuff like:

Fun(Str) when is_string(Str) -> Str;
Fun(Int) when is_integer(Int) -> io:format("~w", [Int]).

or even better use it on messages.

Martin Kristiansen
  • 9,875
  • 10
  • 51
  • 83
  • 4
    No offense but, a better way of testing for strings would be the methods: `io_lib:printable_list/1` and `io_lib:printable_unicode_list/1` used in combination. – Muzaaya Joshua Jun 25 '12 at 08:06
  • See also [this](http://stackoverflow.com/questions/10861347/why-comparing-function-results-is-an-illegal-guard-exception-in-erlang), [this](http://stackoverflow.com/questions/6505213/is-there-a-way-to-use-local-function-in-guard), [this](http://stackoverflow.com/questions/6927632/checking-for-membership-in-an-erlang-guard), [this](http://stackoverflow.com/questions/2241340/unable-to-use-function-call-in-function-guard) and [this](http://stackoverflow.com/questions/7474894/use-of-function-in-guard-not-allowed-suggestions-for-alternate-implementation-w) question. – legoscia Sep 12 '12 at 17:12

2 Answers2

12

You are not allowed to use user defined functions in the guards. It is because the functions in the guards have to be free from side effects (such as using io:format in your functions). In guards, you are limited to the following:

  • BIFs used for type tests (is_atom, is_constant, is_float, is_integer, is_list, is_number, is_pid, is_port, is_reference, is_tuple, is_binary, is_function, is_record),
  • boolean operators (not, and, or, andalso, orelse, ,, ;),
  • relational operators (>, >=, <, =<, =:=, ==, =/=, /=),
  • arithmetic operators (+, -, *, div, rem),
  • bitwise operators (band, bor, bxor, bnot, bsl, bsr),
  • other BIFs that are free of side effects (abs/1, element/2, hd/1, length/1, node/1,2, round/1, size/1, tl/1, trunc/1, self/0)
juro
  • 631
  • 5
  • 13
  • 4
    Hmm, it would seem that checking for side effects would be a pretty easy static-analycis trick... – Martin Kristiansen Jun 24 '12 at 12:03
  • 7
    @MartinKristiansen Not really, because any code in modules can be changed or replaced at runtime. – Alexey Romanov Jun 24 '12 at 15:49
  • @alexeyRomanov: You are absolutely correct -- but perhaps then make a restriction to use BIF's and only functions inside the current module :-) – Martin Kristiansen Jun 25 '12 at 08:23
  • 3
    @MartinKristiansen That would have to be "Only BIFs and those functions inside the current module which call only BIFs and functions inside the current module and aren't recursive (to ensure they terminate)". Which means that you can't use `is_string` as a guard, after all :) – Alexey Romanov Jun 25 '12 at 08:29
  • @MartinKristiansen ...or functions that use special constructs such as `receive` that always perform a side effect. – Magnus Kronqvist Jun 26 '12 at 15:43
  • But if NIFs and C-Nodes are allowed (which can also crash the entire runtime) why aren't custom guards? – toraritte Jul 18 '20 at 14:42
6

Another reason for not allowing user defined functions in guards is that errors are handled differently in guards than in "normal" functions. In a guard an error does not generate an exception, it only causes the guard itself to fail.

Guards are not really expressions but tests.

rvirding
  • 20,848
  • 2
  • 37
  • 56