3

I am new to Erlang. I just want to reassign a value to a string variable:

get_alert_body(Packet) ->
    BodyElement = element(8,Packet),
    Body = "my text",
    Els = xmpp:get_els(Packet),
    lists:foreach(fun(El) ->
        ElementName = io_lib:format("~s",[xmpp:get_name(El)]),
        IsFile = string:equal(ElementName,"fileType"),
        if
            IsFile ->
                FileType = fxml:get_tag_cdata(El),
                IsPhoto = string:equal(FileType,"photo"),
                IsVideo = string:equal(FileType,"video"),
                if
                    IsPhoto ->
                        %% if it gets to this I would like to return "my photo"
                        Body = "my photo";
                    IsVideo ->
                        %% else if it gets to this I would like to return "my video"
                        Body = "my video";
                    true ->
                        %% otherwise I would like to return "my text"
                        ok
                end;
            true ->
                ok
        end
    end, Els),
    Body.

But I get this error:

error,{badmatch,"test2"}

Even if I do somthing like:

A = "test1",
A = "test2",

I get the same error.

I would appreciate your help.

2240
  • 1,547
  • 2
  • 12
  • 30
Besat
  • 1,428
  • 13
  • 27

3 Answers3

6

You can't. Erlang has a feature called "single assignment", meaning that you can't change the value of a variable after assigning it for the first time. If you try to do that, it becomes a pattern match instead, and you get a badmatch error just like if you try to do "test1" = "test2".

Your example can be written as:

A =
  if
      Condition ->
          "test2";
      true ->
          "test1"
  end

See this question and its answers for more about single assignment.


In your expanded example, what you're trying to do can be achieved with a fold. That is, given a list and an additional value called an "accumulator", go through the list and call a function for each element, and let the return value of that function be the new accumulator - and return the accumulator in the end.

Use lists:foldl/3 for that, like this:

get_alert_body(Packet) ->
    BodyElement = element(8,Packet),
    DefaultBody = "my text",
    Els = xmpp:get_els(Packet),
    lists:foldl(fun(El, Body) ->
        ElementName = io_lib:format("~s",[xmpp:get_name(El)]),
        IsFile = string:equal(ElementName,"fileType"),
        if
            IsFile ->
                FileType = fxml:get_tag_cdata(El),
                IsPhoto = string:equal(FileType,"photo"),
                IsVideo = string:equal(FileType,"video"),
                if
                    IsPhoto ->
                        %% if it gets to this I would like to return "my photo"
                        "my photo";
                    IsVideo ->
                        %% else if it gets to this I would like to return "my video"
                        "my video";
                    true ->
                        %% otherwise return the existing value
                        Body
                end;
            true ->
                ok
        end
    end, DefaultBody, Els).
legoscia
  • 39,593
  • 22
  • 116
  • 167
  • Thank you so much. I will edit my sample code.. Since I am confused how to use it with a loop. – Besat Feb 06 '19 at 15:29
  • I really appreciate it.. I ended up using a different solution but your response helped me a lot. – Besat Feb 06 '19 at 16:17
1

= is not an assignment operator in erlang, rather it's a pattern match operator.

If you write:

{A, hello} = {10, hello}.

and A has not been bound to (or you can say assigned) a value yet, then erlang attempts to create a match for the right hand side (because = is the match operator). To create a match for the right hand side, erlang binds/assigns A the value 10 (for fun you can assume that *&* is the assignment operator in erlang, so erlang does A *&* 10 to assign 10 to the variable A).

It works the same way if you write:

B = 10,

In order for erlang to create a match for the right hand side, erlang assigns B the value 10: B *&* 10, and the match succeeds, so execution continues on the next line. However, if you write:

C = 3,
C = 22,

then in the first line, erlang assigns the value 3 to C: C *&* 3 to find a match for the right hand side (because = is the match operator). But the second line is equivalent to:

3 = 22,

And the error message will say that the match operator = failed because there is no possible way to make the left hand side match the right hand side 22.

7stud
  • 46,922
  • 14
  • 101
  • 127
0

Variables in Erlang can't be reassigned, but they can be "shadowed" to achieve a similar effect. In this example, the variable X is "redefined" as X ++ Y within the scope of an anonymous function:

-module(main).
-export([main/1]).

main([_]) ->
    X = "X",
    Y = "Y",
    fun(X) -> io:fwrite(X) end(X++Y),
    init:stop().

This program prints XY instead of X, since X is the name of the anonymous function's parameter.

Anderson Green
  • 30,230
  • 67
  • 195
  • 328