Sometime I am running into the situation when a struct should be created using new function. Since %T{} is a popular way to create a struct, use of it becomes a quick road to hell (since it produces unclear messages in unexpected places). For example, if a field needs to be initialized with make_ref, I found no good way to avoid new.
I know, i could make structure @opaque, but it make the whole struct opaque. Is there a good way to make %T{} constructor inaccessible?
Thank you,
/vadim
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/e38bd60b-f8a7-4183-bb17-ca0a8def19ea%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Nothing is ever easy. I followed your suggestion, and the result is a bit disappointing:
macro in Kernel:
defmacro defstructp(fields) do
quote bind_quoted: [fields: fields] do
fields = Kernel.Utils.defstruct(__MODULE__, fields)
@struct fields
case Module.get_attribute(__MODULE__, :derive) do
[] -> :ok
derive -> Protocol.__derive__(derive, __MODULE__, __ENV__)
end
defp __struct__() do
@struct
end
fields
end
end
definition of the struct:
defmodule StructTest do
defstructp [:aaa, :bbb]
def new do
%__MODULE__{aaa: 1, bbb: 2}
end
end
produces
== Compilation error on file lib/test_struct.ex ==
** (CompileError) lib/test_struct.ex:5: StructTest.__struct__/0 is undefined, cannot expand struct StructTest
(elixir) src/elixir_map.erl:58: :elixir_map.translate_struct/4
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/358207e9-309d-4531-876e-8e77978f31db%40googlegroups.com.
There is a few aspects of openness (I am trying to list, but I do not mean it to be implemented) in order of openness:
%MyStruct{} or struct(MyStruct));We currently have two options defined: defstruct and @opaque. (I was a bit surprised when I experimented with opaque since protection seems to be limited to the type, not to the implementation. I’m not sure what value does it provide.)
I am not sure why, but my reading of match did not include instantiation of struct. After all, struct might include default values of fields that do not participate in the match, or binding of variables (the later could be a syntactic sugar).
defmodule Foo do
defstruct foo: 123,bar: 345
def match do
%__MODULE__{foo: 345} = struct(__MODULE__, foo: 345, bar: 123)
end
end
From system development standpoint, I can see advantage of open and close by module structs. Intermediate position takes a struct open for application, but closed outside application. The closed struct can be manipulated only by provided functions (e.g., one can think that struct fields are mangled beyond recognition). Interestingly, I would think that %Foo{} = x match would work (without any field specified) being equivalent of x.__struct__ == Foo.
My original question (regarding explicit creation) is modification of normal struct creation. Syntactically I would think more along line of @explicit_new attribute of the defstruct macro than making struct closed. It is difficult to judge merits of it. Too many options is a clearly bad choice for the language. If given a choice of what to implement, I would vote for module/application closed structs over explicit new. Even for myself, the situation looks a bit fragile: we do not allow to create empty struct, but we allow to modify any field afterward.
I would appreciate clarification regarding Ecto.Query. I do not know what is the problem about it.
One more item: I can see situation when we do not want match available. An example use case would be a function which accepts/returns several variants of data, a we do not want its client to rely on particular one. It looks like strong opaque should work (but it is stronger than current opaque).
%MyStruct{} or struct(MyStruct));what if we will set __struct__ to {MyStruct, :opaque}? Can we hide fields without much overhead?
/vadim
Can we extend it? If we introduce a new feature, it does not break existing code. It could breaks only new code.
I would admit: it is possible that we pass new struct to some preexisting code. It is still new development, and can be decided explicitly by caller.
/vadim
Ben: I think the actual design question here is "How do I provide dynamic defaults for data structures?"
There's also the caveat that these data structures are not valid without these dynamic defaults.
I don't think that's necessarily conflating with OOP, but it is a tough thing to solve.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/868be6c8-2a4a-41b6-8e8c-2756a9dd4f69%40googlegroups.com.
If the data openness is concept of the language, I am totally happy. Information hiding is optional thing. I would ask although, why do we have `opaque` types, what is their purpose?
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/aee36d67-9554-4143-8a68-d63a7dbe084e%40googlegroups.com.
The idea is, instead of making new, to allow something like:
defstruct [:foo, :bar, :baz] do
foo = make_ref
end
or
defstruct [:foo, :bar, :baz], fn data ->
data.foo = make_ref
end
potentially inlining passed function.
Then
defmacro defstruct(fields, fun \\ nil) do
quote bind_quoted: [fields: fields] do
fields = Kernel.Utils.defstruct(__MODULE__, fields)
@struct fields
case Module.get_attribute(__MODULE__, :derive) do
[] -> :ok
derive -> Protocol.__derive__(derive, __MODULE__, __ENV__)
end
def __struct__() do
@struct
if fun, do: fun.() ## a lot of details here
end
fields
end
end