Sometimes I want to check if the input into a function is not a blank string.
My first approach:
defmodule Example do def do_stuff(string1, string2) when is_binary(string1) and byte_size(string1) > 0 and is_binary(string2) and byte_size(string2) > 0 do # Do some stuff with string1 and string2, because now we know they cannot be: # * "" # * " " # * " " end end
The problem here is that when a string contains spaces(":space: ", “:space: :another_space:”, etc.) the guard clauses above will not detect it and I reach the body of the function, but what I want is to detect the blank string in the guard clause.
So I tried to make a custom guard:
defmodule BlankGuard do defmacro is_not_blank?(string) do callback = fn <<" " :: binary, rest :: binary>>, func -> func.(rest, func) _string = "", _func -> true _string, _func -> false end is_blank = fn string, cb -> cb.(string, cb) end quote do unquote(string) |> is_binary() and unquote(string) |> byte_size > 0 and unquote(string) |> is_blank.(callback) end end end
And tried to use like this:
defmodule ExampleGuard do import BlankGuard def do_stuff(string1, string2) when is_not_blank?(string1) and is_not_blank?(string2) do # Do some stuff with string1 and string2, because now we know they cannot be: # * "" # * " " # * " " end end
And I get the error:
== Compilation error in file lib/play.ex == ** (CompileError) lib/play.ex:81: invalid expression in guard, anonymous call is not allowed in guards. To learn more about guards, visit: https://hexdocs.pm/elixir/guards.html
Following the link in the error it seems that custom guards can only invoke other guards.
So my question is if I am missing something or if this is not really possible to implement in a guard clause?
I know I can implement this in each module I want to check for a blank string, but looks like unnecessary code repetition:
# @link https://rocket-science.ru/hacking/2017/03/28/is-empty-guard-for-binaries def empty?(<<" " :: binary, rest :: binary>>), do: empty?(rest) def empty?(string = ""), do: true def empty?(_string), do: false