Hey I don't believe that exists, at least not in the standard distribution. Perhaps there is a library that provides this functionality.
Cheers, Louis On Wed, 7 Nov 2018, 23:05 Sergiy Kukunin, <sergey.kuku...@gmail.com> wrote: > Thanks for the explanation about guards, makes sense. And I totally agree > with you, that can be implemented as an assertion at the very begin of > function's body. > > Again, the question - is there a way to leverage typespecs? Is there a way > to implement something like this "hey elixir, is this value corresponds to > this type?" > I'd like to avoid a custom way to define specs for that assertions macros. > > On Thu, Nov 8, 2018 at 12:58 AM Louis Pilfold <lo...@lpil.uk> wrote: > >> Hey >> >> The implementation you've given there is expensive and only works for >> lists up to a certain length. >> >> To solve this one you'll need to step outside of guard clauses as they >> only support a limited subset of Elixir/Erlang. The idea is that all >> operations in guards are very fast and run in constant time, so iterating >> over a list or arbitrary length is not supported. >> >> Another option would be to write a macro that prepends a type checking >> statement to a function body, asserting that the arguements are of the >> correct type. >> >> Cheers, >> Louis >> >> On Wed, 7 Nov 2018, 22:41 Sergiy Kukunin, <sergey.kuku...@gmail.com> >> wrote: >> >>> 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 >>> <https://groups.google.com/d/msgid/elixir-lang-core/CADp0H2jzEf38pTd9E8bxXxK%2BG5tGeZRrj0PjNoC5S7FePpDA5g%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 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/CABu8xFBN_wh-pFWt9XJhmUmm7xgJPQWfLCkT9BGP2A%3DBqYfnKw%40mail.gmail.com >> <https://groups.google.com/d/msgid/elixir-lang-core/CABu8xFBN_wh-pFWt9XJhmUmm7xgJPQWfLCkT9BGP2A%3DBqYfnKw%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/CADp0H2h2sEadOftQMHfDXFsOYzEH5DM81JF1Wcfdtvq9M4SUyA%40mail.gmail.com > <https://groups.google.com/d/msgid/elixir-lang-core/CADp0H2h2sEadOftQMHfDXFsOYzEH5DM81JF1Wcfdtvq9M4SUyA%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/CABu8xFBz%3DhYgsPa0GXqBG_%2BptAcQRUjrusYaQhb0iGaq2QL_Pw%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.