Re: [elixir-core:8369] Re: Runtime typespecs assertion

2018-11-07 Thread Louis Pilfold
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  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,  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, 

Re: [elixir-core:8369] Re: Runtime typespecs assertion

2018-11-07 Thread Sergiy Kukunin
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, > 
> 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