Keyword, etc Syntax Proposal

Hi guys!:grinning:
Recently I learned Elixir and now I’m rewriting my project in it. This language is great and really fun to use.
And I found there are some “if I could do this” things.
So I played with my virtual syntax as below:

  1. Keyword syntactic sugar
# inspired by ECMAScript:
# const { a, b, c } = { a: "A", b: "B", c: "C" }
# const obj = { a, b, c }

:[foo, bar, baz]
# (SyntaxError)v1.6.2, expect it to be interpreted as
# => [foo: foo, bar: bar, baz: baz]

# With multiple keywords, we could do
foo, bar, baz 
# => [foo: foo, bar: bar, baz: baz]
# `foo` is `foo` and would have to use `:[foo]` to be `[foo: foo]`

# In that case, we must be careful not to involve neighbouring
Foo.start_link state, i, short, very_long_keyword
# Gatcha! => Foo.start_link [state: state, i: i, short: short, very_long_keyword: very_long_keyword]

Foo.start_link state, :[i, short, very_long_keyword]
# => Foo.start_link state, i: i, short: short, very_long_keyword: very_long_keyword

# In function params, we would have to use :[] style
def foo(:[bar, baz]), do: something bar, baz 

# for Map
%{foo, bar, baz}
# => %{foo: foo, bar: bar, baz: baz}

# Struct
def to_person(%Plug.Conn{assigns: %{name, age, bio, phone, address}}) do
  %Person{name, age, bio, phone, address}
end

# pattern matching
i, short, very_long_keyword: long = [i: 42, short: "ex", very_long_keyword: "elixir-lang"]
# would match: i = 42, short = "ex", long = "elixir-lang"

# nested
foo, bar, baz: %{a, "b" => b} = [foo: "FOO", bar: "BAR", baz: %{a: "A", "b" => "B"}]
# would match: foo = "FOO", bar = "BAR", a = "A", b = "B"

  1. Guard syntactic sugar
def foo(bar) when is_integer, do: bar + 1
def foo(bar) when is_float,   do: trunc(bar) + 1
def foo(bar) when is_binary,  do: do_binary(bar)
# (ArgumentError) v1.6.2 =>
# def foo(bar) when is_integer(bar), do: ..
# def foo(bar) when is_float(bar),   do: ..
# def foo(bar) when is_binary(bar),  do: ..

# Guard without argument means "catch whole arg"
# but would make sense only with foo/1 function..
  1. Piping extension
# Acceptance of anonymous function
9
|> Foo.plus(1)
|> &Foo.div(1000, &1)
|> &"result is #{&1}"
# (ArgumentError) v1.6.2 =>
# 9
# |> Foo.plus(1)
# |> (&Foo.div(1000, &1)).()
# |> (&"result is #{&1}").()
# "result is 100"

# Create anonymous function by headless piping
my_func = |> Foo.plus(1) |> &Foo.div(1000, &1) |> &"result is #{&1}"
# (SyntaxError) v1.6.2 =>
# my_func = fn i -> i |> Foo.plus(1) |> (&Foo.div(1000, &1)).() |> (&"result is #{&1}").() end

# Gatcha!
def get_my_func() do
  one      = 1
  thousand = 1000
  |> Foo.plus(one) |> &Foo.div(thousand, &1) |> &"result is #{&1}"
end
# would result in normal piping: `1000 |> Foo.plus(..) ..`

I know these would likely increase confusion especially to new comers.
It’s just playing, thanks!:smile:

1 and 3 have been proposed and rejected many times on the elixir core mailing list. However, if you really want that functionality there are libraries that provide that functionality.

Here are some:

https://github.com/wojtekmach/pipe_capture

2 I haven’t seen proposed before. However, I would say generally Elixir tends to favor explicitness over brevity. I wouldn’t be surprised if it was rejected as well. If you wanted to implement it yourself, you could probably write your own macro like def that provided that functionality.

3 Likes

Thank you for your reply and I really appreciate your guidance.

elixir core mailing list

Ah, this is just what I’ve been looking for. Thanks a lot :grinning:

2 Likes