Found another problem: can't express "list of strings" in guards nor pattern matching. It's an easy task for typespecs `[String.t(), ...]`, but I can't check typespecs in runtime. Found a very dirty hack, that works for lists up to 5 strings, enjoy:
defguardp is_list_of_strings(x) when (length(x) == 1 and is_binary(hd(x))) or (length(x) == 2 and is_binary(hd(x)) and is_binary(hd(tl(x)))) or (length(x) == 3 and is_binary(hd(x)) and is_binary(hd(tl(x))) and is_binary(hd(tl(tl(x))))) or (length(x) == 4 and is_binary(hd(x)) and is_binary(hd(tl(x))) and is_binary(hd(tl(tl(x)))) and is_binary(hd(tl(tl(tl(x)))))) or (length(x) == 5 and is_binary(hd(x)) and is_binary(hd(tl(x))) and is_binary(hd(tl(tl(x)))) and is_binary(hd(tl(tl(tl(x))))) and is_binary(hd(tl(tl(tl(tl(x))))))) Wouldn't it be cool to be able to write something like defguard is_list_of_strings(x) match_type([String.t(), ...]) Again, I'm pretty new, and I know nothing about the implementation and where Elixir ends and Erlang starts, and how feasible it is. Just an idea =) On Wed, Nov 7, 2018 at 10:11 PM Sergiy Kukunin <sergey.kuku...@gmail.com> wrote: > Actually, that what I understood only in my last message - I can implement > it right now. I'm pretty new to Elixir, so that wasn't obvious to me. > > Currently, it seems it's resolved, there are only suggestions to improve > syntax, that are too minor. > > Thank everyone for assistance > > On Wed, Nov 7, 2018 at 9:15 PM Louis Pilfold <lo...@lpil.uk> wrote: > >> Hi Sergiy >> >> I'm afraid I don't follow. From what I understand of your proposal the >> current defguard system meets your needs- what are you looking to add? >> >> Cheers, >> Louis >> >> On Wed, 7 Nov 2018 at 18:38 Sergiy Kukunin <sergey.kuku...@gmail.com> >> wrote: >> >>> 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 <sergey....@gmail.com> >>>> 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, <ivan.y...@gmail.com> 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 elixir-lang-co...@googlegroups.com. >>>>>> >>>>>> >>>>>>> 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 elixir-lang-co...@googlegroups.com. >>>>> >>>> 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 elixir-lang-core+unsubscr...@googlegroups.com. >> >> >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/elixir-lang-core/f6a0f326-ffa4-4b69-998d-6f60a91abe87%40googlegroups.com >>> <https://groups.google.com/d/msgid/elixir-lang-core/f6a0f326-ffa4-4b69-998d-6f60a91abe87%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 a topic in the >> Google Groups "elixir-lang-core" group. >> To unsubscribe from this topic, visit >> https://groups.google.com/d/topic/elixir-lang-core/fvn29FjvSks/unsubscribe >> . >> To unsubscribe from this group and all its topics, send an email to >> elixir-lang-core+unsubscr...@googlegroups.com. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/elixir-lang-core/CABu8xFBC%3DM6s0p9po2CsoWXQ-j0gRRiyNdGms13YBUt4-sC%2BMg%40mail.gmail.com >> <https://groups.google.com/d/msgid/elixir-lang-core/CABu8xFBC%3DM6s0p9po2CsoWXQ-j0gRRiyNdGms13YBUt4-sC%2BMg%40mail.gmail.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 elixir-lang-core+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CADp0H2jzEf38pTd9E8bxXxK%2BG5tGeZRrj0PjNoC5S7FePpDA5g%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.