I afraid you missed my point, I might have expressed it poorly. Let's 
assume I have a simple type: {is_atom(), is_number(), is_binary()}. I want 
to define a guard to match it. Without reusing I can write a function 
accepting it:

func({x, y, z}) when is_atom(x) and is_number(y) and is_binary(z), do: true

but then I want to define another function which expects the same tuple:

another({x, y, z}) when is_atom(x) and is_number(y) and is_binary(z), do: 
true

I don't have a way to define a custom guard to match tuple elements since 
there is no pattern matching in defguard nor there is `elem` in guards. So 
both options don't work:

defguard is_mytype({x, y, z}) when is_atom(x) and is_number(y) and 
is_binary(z)

nor

defguard is_mytype(x) when is_atom(elem(x, 0)) and is_number(elem(x, 1)) 
and is_binary(elem(x, 2))

Furthermore, I would want to define a function that receives a value of my 
type inside of complex structure: 

function({:ok, {x, y, z}}) when is_atom(x) and is_number(y) and 
is_binary(z), do: true

it would be cool to have it defined as

function({:ok, x}) when is_mytype(x), do: true

P.S. Actually, I've found that `elem` works in guards, so I can define my 
guard without pattern matching. That's good for now, but

func({x, y, z}) when is_atom(x) and is_number(y) and is_binary(z), do: true

sounds cooler, IMHO =)

On Wednesday, November 7, 2018 at 8:20:22 PM UTC+2, Louis Pilfold wrote:
>
> Hi Sergiy
>
> The functionality you've described can be implemented with macros, no need 
> to modify Elixir or Erlang.
>
> To start it could be as simple as defining guards that assert nothing in 
> the production environment.
>
> defmodule Test do
>   if Mix.env() == :prod do
>     defguard is_my_type(x) when true
>   else
>     defguard is_my_type(x) when is_atom(x)
>   end
>
>   def go(x) when is_my_type(x) do
>     x
>   end
> end
>
> This could be a little error prone though as unless you remember to apply 
> the guard to every clause of the function your logic may change when they 
> are removed. Even if you apply them to every clause if you use exceptions 
> as flow control you may run into problems as values that previously would 
> result in a FunctionClauseError would be passed though.
>
> Plenty to think about! Perhaps experiment with a little proof of concept 
> library and see what happens :)
>
> Cheers,
> Louis
>
> On Wed, 7 Nov 2018 at 17:44 Sergiy Kukunin <[email protected] 
> <javascript:>> wrote:
>
>> Thanks for the answers. Just want to note, that I don't want to invent 
>> type system such as in statically typed languages. I mean more about 
>> defining schemas we can check different values with. All pattern matching, 
>> guards and typespec might work for this. Furthermore, it would be cool to 
>> make it composable and reusable (such as defguards and typespecs right now).
>>
>> Just to conclude, I would suggest that either of these would improve the 
>> safety and convenience of the language:
>> - allow pattern matching in custom guards (either via the built-in guard 
>> such as `Kernel.match?/2` or by extending the defguard syntax)
>> - having a macro to check whether a value corresponds to a defined @type
>>
>> What's about such syntax?
>>
>>  defguard is_mytype({x, y}) when is_atom(x) and is_number(y)
>>
>>  def test({:ok, value}) when is_mytype(value), do: true
>>  def test(_), do: false
>>
>>  test({:ok, {:hello, 5}}) # should be true
>>  test({:ok, {2, 5}})  # should be false
>>
>> There are a couple of reasons I've raised this question:
>>
>> - do I miss something? don't I try to solve the problem in a wrong way?
>> - to estimate how hard is it to implement in a 3rd-party library or does 
>> it require changes to core Elixir/ErlangVM
>>
>> Thanks
>>
>>
>> On Wednesday, November 7, 2018 at 7:20:47 PM UTC+2, Louis Pilfold wrote:
>>
>>> Hi all
>>>
>>> The desire for more safety in Elixir is reasonable, both at compile time 
>>> and at runtime.
>>>
>>> The core team have previously experimented with introducting a compile 
>>> time type checking system, and we also have the dialyser and gradualizer 
>>> tools that can be used with Elixir.
>>>
>>> Checking at runtime is something we already do in Elixir and Erlang 
>>> through the use of pattern matching and guards such as `is_binary/1`.
>>> A library of macros that automates these checks could be an interesting 
>>> project, perhaps an area worth exploring for members of the community.
>>>
>>> Cheers,
>>>
>> Louis
>>>
>>> On Wed, 7 Nov 2018, 16:46 Ivan Yurov, <[email protected]> wrote:
>>>
>> If you want type-safety why not to just pick a strongly typed language, 
>>>> like Ocaml for example? Elixir is bound to Erlang VM and will never 
>>>> provide 
>>>> any features like you're describing that are not supported by Erlang. And 
>>>> I 
>>>> don't think type-checking ever happens at runtime in any language.
>>>>
>>>> On Wednesday, November 7, 2018 at 12:00:53 PM UTC+1, Sergiy Kukunin 
>>>> wrote:
>>>>>
>>>>> Hello there. This is my first message to the elixir group. Thanks for 
>>>>> the great language.
>>>>>
>>>>> While I'm writing my code, I want to make functions to be safer. It's 
>>>>> bad practice if a function accepts unexpected input and pass it further, 
>>>>> and it blows in a completely different part of a system.
>>>>>
>>>>> At first glance, I have pattern matching, but it's pretty limited. It 
>>>>> becomes really powerful in conjunction with guards, so I can write a 
>>>>> signature to match literally everything. 
>>>>> But they hard to re-use, If I have multiple functions operating with 
>>>>> the same object. Yes, I can define a custom guard, but can I use pattern 
>>>>> matching there? `Kernel.match?/2` doesn't work, so I'm limited with only 
>>>>> guards in my custom guards.
>>>>>
>>>>> Another thing that we have typespecs. It seems exactly what I'm 
>>>>> looking for: you have a wide set of built-in types, and I can easily 
>>>>> compose and reuse my own types. The problem with it, that it doesn't 
>>>>> affect 
>>>>> runtime. I know about static analyzer `dialyzer`, but I'm not sure it 
>>>>> will 
>>>>> catch all cases since it's a static check, not a runtime.
>>>>>
>>>>> Let's assume a simple function, that wraps a value into a list:
>>>>>
>>>>>   @spec same(number()) :: [number()]
>>>>>   def same(number) do
>>>>>     [number]
>>>>>   end
>>>>>
>>>>> I'm sure the `dialyzer` won't complain since a signature is valid. But 
>>>>> what if I do: `same("abc")` ? What will prevent Elixir from returning a 
>>>>> wrong type? I guess, nothing. 
>>>>> An example from a real life: I have a function, that accepts a custom 
>>>>> shaped value (using tuples) and feeds it to a queue. Then, in the totally 
>>>>> different part of the system, a consumer gets values from the queue. And 
>>>>> when a wrong value was fed on the producer side, it blows on the consumer 
>>>>> side. So I decided to put some constraints on the producer side to fail 
>>>>> fast.
>>>>>
>>>>> Yes, I could define a guard, but again, if I have a pretty complex 
>>>>> type instead of the simple `number`, I had to duplicate the type 
>>>>> defining: 
>>>>> one for typespec, another is for a custom guard (which is limited, since 
>>>>> I 
>>>>> can't use pattern matching there).
>>>>>
>>>>> Wouldn't it be cool, If we had a mechanism to assert a value to its 
>>>>> type, in runtime? To avoid performance penalty we could enable it only 
>>>>> for 
>>>>> runtime. Is there a way right now to check whether a value corresponds to 
>>>>> a 
>>>>> type in runtime? Can I implement a custom macro to provide a good DSL for 
>>>>> this? Is it helpful at all?
>>>>>
>>>>> P.S. You may say, use structs and pattern matching would work in this 
>>>>> case. But what if my type is better represented by a tuple: {atom(), 
>>>>> pos_integer(), string()}. Converting it to a struct might complicate a 
>>>>> way 
>>>>> to work with the value.
>>>>>
>>>> -- 
>>>> 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 [email protected].
>>>
>>>
>>>> To view this discussion on the web visit 
>>>> https://groups.google.com/d/msgid/elixir-lang-core/8c4d9dac-134d-471c-a402-e9696bf5aecf%40googlegroups.com
>>>>  
>>>> <https://groups.google.com/d/msgid/elixir-lang-core/8c4d9dac-134d-471c-a402-e9696bf5aecf%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>> .
>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>> -- 
>> 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 [email protected] <javascript:>.
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msgid/elixir-lang-core/c7e602a5-a694-46f9-99a5-983b4d50eea0%40googlegroups.com
>>  
>> <https://groups.google.com/d/msgid/elixir-lang-core/c7e602a5-a694-46f9-99a5-983b4d50eea0%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>> For more options, visit https://groups.google.com/d/optout.
>>
>

-- 
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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/f6a0f326-ffa4-4b69-998d-6f60a91abe87%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to