Translate

miércoles, 1 de mayo de 2024

If de Erlang


Los if actúan como Guards y comparten la sintaxis, pero en el cuerpo de la función. De hecho, las cláusulas if se llaman patrones de Guards. Para ver qué tan similar a las Guards es la expresión if, miremos los siguientes ejemplos:


-module(what_the_if).

-export([heh_fine/0]).


heh_fine() ->

    if 1 =:= 1 ->

        works

    end,

    if 1 =:= 2; 1 =:= 1 ->

        works

    end,

    if 1 =:= 2, 1 =:= 1 ->

        fails

    end.


Guarde esto como what_the_if.erl y probémoslo:


1> c(what_the_if).

./what_the_if.erl:12: Warning: no clause will ever match

./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false'

{ok,what_the_if}

2> what_the_if:heh_fine().

** exception error: no true branch found when evaluating an if expression

     in function  what_the_if:heh_fine/0


¡UH oh! el compilador nos advierte que ninguna cláusula del if en la línea 12 (1 =:= 2, 1 =:= 1) coincidirá porque su única protección se evalúa como falsa. Recuerde, en Erlang todo tiene que devolver algo, y las expresiones if no son una excepción a la regla. Como tal, cuando Erlang no puede encontrar una manera de que un Guards tenga éxito, se bloqueará: no puede no devolver algo. Como tal, necesitamos agregar una rama general que siempre tendrá éxito pase lo que pase. En la mayoría de los lenguajes, esto se llamaría "else". En Erlang, usamos 'true' dado que si una condición no es verdadera, true siempre lo será:


oh_god(N) ->

    if N =:= 2 -> might_succeed;

       true -> always_does  %% this is Erlang's if's 'else!'

    end.


Y ahora si probamos esta nueva función (la antigua seguirá escupiendo advertencias, las ignorará o las tomará como recordatorio de lo que no se debe hacer):


3> c(what_the_if).

./what_the_if.erl:12: Warning: no clause will ever match

./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false'

{ok,what_the_if}

4> what_the_if:oh_god(2).

might_succeed

5> what_the_if:oh_god(3).

always_does


Aquí hay otra función que muestra cómo usar muchos Guards en una expresión if. La función también ilustra cómo cualquier expresión debe devolver algo: Talk tiene el resultado de la expresión if vinculado y luego se concatena en una cadena, dentro de una tupla. Al leer el código, es fácil ver cómo la falta de una rama verdadera arruinaría las cosas, considerando que Erlang no tiene un valor nulo (es decir, NIL de Lisp, NULL de C, None de Python, etc.):


%% note, this one would be better as a pattern match in function heads!

%% I'm doing it this way for the sake of the example.

help_me(Animal) ->

    Talk = if Animal == cat  -> "meow";

              Animal == beef -> "mooo";

              Animal == dog  -> "bark";

              Animal == tree -> "bark";

              true -> "fgdadfgna"

           end,

    {Animal, "says " ++ Talk ++ "!"}.


Y ahora lo probamos:


6> c(what_the_if).

./what_the_if.erl:12: Warning: no clause will ever match

./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false'

{ok,what_the_if}

7> what_the_if:help_me(dog).

{dog,"says bark!"}

8> what_the_if:help_me("it hurts!").

{"it hurts!","says fgdadfgna!"}


Todo este horror expresado por los nombres de las funciones en what_the_if.erl se expresa con respecto a la construcción del lenguaje if cuando se ve desde la perspectiva del if de cualquier otro lenguaje. En el contexto de Erlang, resulta ser una construcción perfectamente lógica con un nombre confuso.