Depends how you write code in the other languages you mentioned. It’s not a significant switch if you already write modular function based code in the other languages.
You can do stuff like the below to “mutate” an original value even though you are technically replacing it to align with how you likely code normally.
def update(%{record: record} = assigns, socket) do
socket = socket
|> assign(:flash_map, flash_map())
form_update(Group, record, assigns, socket)
end
Technically the above is same as the below, but on paper in the above you have “mutated” the original socket in an immutable way. The main difference with the below is that you could access the original socket by using socket after the updated_socket has been created. It’s not used in the below, but it’s fairly common in Elixir to take a starting value, and use it multiple times to create new values without changing the starting value.
def update(%{record: record} = assigns, socket) do
updated_socket = socket
|> assign(:flash_map, flash_map())
form_update(Group, record, assigns, updated_socket)
end
If you did the next example however, the original socket will be used, because all thats happening is the value of the socket is being updated, but never assigned to a new immutable value.
Below, the socket is never replaced, by using something = socket, so even though the socket has the flash_map assigned to it, it never actually updates the original socket value.
def update(%{record: record} = assigns, socket) do
socket
|> assign(:flash_map, flash_map())
form_update(Group, record, assigns, socket)
end
With regards to functional programming, most people are likely here because of how simple it is to follow so you will unlikely have many issues.
If you look at the example below it shows a basic functional example. You pass some params, check the values, call a function based on the result, repeat until the flow is complete. Functional programming allows you to break code down into bitesize reusable chunks like the increment_reply_count.
##########################################################
#### Reply Count : Handle | Create | Increment ####
##########################################################
defp create_redix_reply_metrics(key, ancestor_id, parent_id, date) do
case Redix.command(:redix, ["EXISTS", key]) do
{:ok, 0} ->
comment = Replies.get_reply(ancestor_id, parent_id, PhxieScylla.convert_date(date))
if comment do
Redix.pipeline(:redix, [
["HSET", key, "vote_count", comment["vote_count"]],
["HSET", key, "vote_milestone", comment["vote_milestone"]],
["HSET", key, "reply_count", comment["reply_count"]],
# Primary Key Values
["HSET", key, "ancestor_id", ancestor_id],
["HSET", key, "parent_id", parent_id],
["HSET", key, "created_at", date]
])
else
{:error, :no_comment}
end
_ ->
:ok
end
end
#################################
#### Reply Count : Handle ####
#################################
def handle_reply_count(reply_id, ancestor_id, parent_id, date) do
key = "reply:#{reply_id}:metrics"
case Redix.command(:redix, ["HGETALL", key]) do
{:ok, []} -> create_reply_count(key, ancestor_id, parent_id, date)
{:ok, _} -> increment_reply_count(key)
end
end
#################################
#### Reply Count : Create ####
#################################
defp create_reply_count(key, ancestor_id, parent_id, date) do
with {:ok, _metrics} <- create_redix_reply_metrics(key, ancestor_id, parent_id, date) do
increment_reply_count(key)
end
end
###################################
#### Reply Count : Increment ####
###################################
defp increment_reply_count(key) do
Redix.command(:redix, ["HINCRBY", key, "reply_count", "1"])
end
Also, another basic explanation and example of mutable vs immutable:
Mutable = Update Value
Immutable = Replace Value
if x = 1
Mutable:
x + 1 = 2
x is now 2
Immutable:
x + 1 = y
y is used in place of x as a replaced value
When you write functions with immutable values, you basically just create new values each time, whilst keeping the original value intact.
Rather than change the original value, you create an updated copy based on the original value whilst leaving the original value so that it can be used elsewhere in the code.