nahiyan

nahiyan

Why does the order of session, redirect, flash, etc matter?

Let’s say I want to put a flash message, a session item, and redirect the user. Here’s my code:

conn
|> put_flash(:info, "Login successful!")
|> put_session(:current_user_id, user.id)
|> redirect(to: Routes.page_path(conn, :index))

All 3 of these functions just toss around the “conn” variable, modifying it in the process.

Now, if I change the order of the function, such as like this:

conn
|> put_session(:current_user_id, user.id)
|> redirect(to: Routes.page_path(conn, :index))
|> put_flash(:info, "Login successful!")

The flash message gets lost along the way. Why does it make a difference? Aren’t we just defining the action through the “conn” variable?

Marked As Solved

peerreynders

peerreynders

Welcome to the forum!

By the time Phoenix.Controller.redirect/2 returns the 302 HTTP response has already been sent.

https://github.com/phoenixframework/phoenix/blob/v1.4.10/lib/phoenix/controller.ex#L392-L400

So modifying the conn structure afterwards will have no effect on the response.

You can’t saddle the horse when it has already bolted out of the barn.

Also Liked

peerreynders

peerreynders

Haskell (and Elm) is singleminded in its pursuit of purity. Functions like put_session and redirect could exist but rather than directly (and rather imperatively) interacting with the HOW (horrible outside world) those functions would simply register the intent to interact with the HOW somewhere inside the conn. It’s only when the completed conn is handed over to the runtime that those interactions with the HOW happen under the runtime’s full control.

The Clojure community often talks about how immutability helps them to push side effects to the edge of the system. This effect is captured architecturally in the Ports and Adapters architecture (see Functional architecture is Ports and Adapters by Mark Seemann).

The way put_session and redirect in Phoenix work aren’t consistent with a functional core imperative shell kind of approach and could be surprising to somebody coming from a background of functional design which practices a higher level of purity.

peerreynders

peerreynders

well it must be really tedious to do anything in Haskell.

It would be tedious to do something like that in Erlang/Elixir.

Given how Haskell embraces monads (less intrusively in Elm) it is actually relatively simple but it does require a significant shift in thinking. “Regular” programming is like flying the plane yourself - programming in Haskell is more like filing a flight plan and having the autopilot do all the dirty work.

Where is the side effect? Where the HOW gets in the game for that case?

  • put_session stores information in session storage which depending on configuration is either Plug.Session.ETS or Plug.Session.COOKIE. Putting a value into an ETS table is interacting with an external mutable entity and therefore classifies as a side effect.
  • redirect sends the response. To do that it has to interact with the web server which is part of the HOW. And sending the response is a side effect.

Update: looking at the code put_session simply updates the conn struct - so the interaction with the session store is actually happening at the edges (via the plug instantiation).

It’s the call to the session plug that fetches the state from the session store and places it in the conn. At the same time before_send is registered to save the session state back to the store just before the response is sent.

https://github.com/elixir-plug/plug/blob/v1.8.3/lib/plug/session.ex#L60-L104

nahiyan

nahiyan

I was about to write all these to you, but peerreynders wrote it much better than I could. Let me explain why I was confused. The main issue here is that most of the functions take the Plug.Conn struct as an input and generates a new (slightly different) version of it as an output; there was no side-effect, not even in the put_session function. However, the redirect function was different. It had side effects - that is, sending the response to the user through the server. I was too blind to side-effects, assumed that a functional language would at least try to avoid it until the very edge. I thought the side-effects would be declared through the Plug.Conn struct, but only invoked after it is returned.

Should have checked the docs before I started this thread, but it’s nice to see people discuss about this and explain the situation to me. Really liked Elixir and Phoenix’s community, you guys are great!

Where Next?

Popular in Questions Top

nobody
How to bind a phoenix app to a specific ip address? could not find anything about that, nowhere, unfortunately, but for me this is quite...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39467 209
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement