Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Destructure Structures #2

Closed
Havvy opened this issue Nov 15, 2016 · 4 comments
Closed

Destructure Structures #2

Havvy opened this issue Nov 15, 2016 · 4 comments

Comments

@Havvy
Copy link

Havvy commented Nov 15, 2016

defmodule Foo do
  defstruct bar: 0

  import Destructure

  def take_foo(d%Foo{bar}) do
    :ok
  end
end

This fails to compile. I would like it to compile, and have bar in scope in take_foo/1.

@Havvy
Copy link
Author

Havvy commented Dec 12, 2016

This needs to be re-opened (I don't have permission) for three reasons:

  1. When trying to build a struct this way, the struct name doesn't get set.
defmodule Foo do
  defstruct bar: 0

  import Destructure

  def new(bar), do: d%Foo{bar}
end

assert %Foo{bar: 1} == Foo.new(1)

Specifically, Foo.new(1) return %{bar: 1}.

  1. Destructuring a different structure is possible.
defmodule Baz
  defstruct bar: 0
end

assert d(%Foo{bar}) = %Baz{bar: 1}

This assert should fail. It doesn't. Meaning that a literal there doesn't provide (runtime) type safety.

  1. Can't match on the struct name.
d(%x{}) = %Foo{}
assert x == Foo

Currently this returns

** (FunctionClauseError) no function clause matching in Destructure.d/1
    (destructure) expanding macro: Destructure.d/1

@danielberkompas
Copy link
Owner

2 and 3 are valid points, so I'm reopening.

However, your first point is not. This code:

def new(bar), do: d%{bar}

Compiles down to:

def new(bar), do: %{bar: bar}

Nowhere in either version is the struct specified, so it isn't surprising that the new/1 function returns a map, not a struct. Elixir doesn't treat the new function any different than any other function: to Elixir, it's just a function called new.

@Havvy
Copy link
Author

Havvy commented Dec 12, 2016

Point 1 is valid. I just forgot to put the Foo in there. Edited to fix that.

For an iex example (using A because I had Foo defined already prior):

iex(18)> defmodule A do
...(18)> defstruct bar: 0
...(18)> 
...(18)> def new(bar), do: d%A{bar}
...(18)> end
{:module, A,
 <<70, 79, 82, 49, 0, 0, 9, 140, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 233,
   131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
   95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:new, 1}}
iex(19)> A.new(2)
%{bar: 2}

@muhifauzan
Copy link
Contributor

I see your point. I have to admit, my implementation is rather clumsy. I'll try to submit the patch soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants