Why expression `{:ok, v} = something; v` as an argument is a disallowed syntax

Trying to improve my understanding of expressions. Thought that would be possible to express

file_stat = %File.Stat{
  size: 2,
  type: :regular,
  access: :read_write,
  atime: {{2023, 3, 24}, {11, 46, 44}},
  mtime: {{2023, 3, 23}, {16, 54, 17}},
  ctime: {{2023, 3, 23}, {16, 53, 28}},
  mode: 33206,
  links: 1,
  major_device: 3,
  minor_device: 0,
  inode: 0,
  uid: 0,
  gid: 0
}
ctime= file_stat.ctime
{:ok, d} = Date.from_erl(elem(ctime,0))
{:ok, t} = Time.from_erl(elem(ctime,1))
{:ok, dt} = DateTime.new(d, t)
DateTime.diff(DateTime.utc_now(), dt, :hour)

later portion as something like

DateTime.diff(DateTime.utc_now(), 
              {:ok, dt} = DateTime.new(
                 {:ok, d} = Date.from_erl(elem(ctime,0)); d,
                 {:ok, t} = Time.from_erl(elem(ctime,1)); t); dt, :hour)

Certainly that doesn’t make it shorter in any way! I was simply curious why this is a disallowed syntax.

In a nutshell, you cannot have blocks (i.e. multiple expressions separated by ; or newline) as arguments to functions (unless you explicitly wrap them in parens).

4 Likes

:rofl: Thanks @josevalim

That indeed works

DateTime.diff(DateTime.utc_now(), 
              ({:ok, dt} = DateTime.new(
                ({:ok, d} = Date.from_erl(elem(ctime,0)); d),
                ({:ok, t} = Time.from_erl(elem(ctime,1)); t)); dt), :hour)
1 Like

Oh my goodness, that syntax looks mildly…horrifyingly incomprehensible? :laughing:

2 Likes

I am sorry, but have you seen Lisp?

# It is actually valid elixir syntax
(DateTime.diff (DateTime.utc_now), 
              ({:ok, dt} = (DateTime.new ({:ok, d} = (Date.from_erl (elem ctime,0)); d),
                ({:ok, t} = (Time.from_erl (elem ctime, 1)); t)); dt), :hour)

I did do some clojure at some point, but there are too many types of syntax in here for me to parse appropriately. :slight_smile: Perhaps ChatGPT would do better than me at this.

1 Like

Yeah, I had a similar reaction too. I think using the function! variants that returns the unwrapped result or raises would definitely improve readibility as well as pattern matching out the date and time from ctime and declaring an intermediate variable for the result of DateTime.new/3.

{date, time}  = ctime
date_time = DateTime.new!(Date.from_erl!(date), Time.from_erl!(time), :hour)

# or alternatively, maybe a nice place to use `then/2` to transform `ctime` into `date_time`
date_time = ctime |> then(fn {date, time} -> 
  DateTime.new!(Date.from_erl!(date), Time.from_erl!(time), :hour) end)

DateTime.diff(DateTime.utc_now(), date_time)
2 Likes

That’s a good one! :smiley:

THEN! :smiley:

Beautiful, thank you @codeanpeace

1 Like