> @Jose Thanks for the quick reply! I agree that this would provide a better experience for Elixir. I'd be happy to contribute, but I'm very new to the language. I may be able to do it with some hand-holding if you're up for it.
Sure, the first step is to get Elixir test suite passing locally. You can follow the steps here: https://github.com/elixir-lang/elixir/#compiling-from-source Next, you should find the source for String.replace/3 and change things like this: def replace(subject, pattern, replacement, options) To this: def replace(subject, pattern, replacement, options) when is_list(options) Or similar. This is not a step by step guide, so feel free to ask more questions. > For context with another language, if I call "1234".gsub(:nope, "banana") in Ruby, I get this error: [...] In Python, "1234".replace(1234, "banana", 1) produces this To be fair, those are not good examples because those methods are implemented in C in those languages, and languages typically do type checking when crossing the C boundary (otherwise you can end-up with segmentation faults or similar). String.replace/3 is implemented in pure Elixir which is why the error can happen deep inside the stacktrace. A similar example in Ruby would likely raise a NoMethodError on one of the objects given as parameters or lead to something like "TypeError: no implicit conversion of Symbol into Integer" for something like "string"[:global]. I am not saying this to excuse the behaviour but rather to say that a similar behaviour to our current one will also be frequently found in other dynamic languages whenever you go past the built-in functions. *José Valim* www.plataformatec.com.br Skype: jv.ptec Founder and Director of R&D On Sun, Sep 15, 2019 at 5:42 PM Landon Schropp <[email protected]> wrote: > @Jose Thanks for the quick reply! I agree that this would provide a better > experience for Elixir. I'd be happy to contribute, but I'm very new to the > language. I may be able to do it with some hand-holding if you're up for it. > > @Dmitry I disagree—I don't think this error message is clear. > String.replace/4 is calling Keyword.get/3 somewhere under the hood, and > that's where the error message is being thrown. As someone who's new to the > language, it's hard for me to tell what I did wrong when calling the method. > > For context with another language, if I call "1234".gsub(:nope, "banana") > in Ruby, I get this error: > > Traceback (most recent call last): > 1: from test.rb:1:in `<main>' > test.rb:1:in `gsub': wrong argument type Symbol (expected Regexp) > (TypeError) > > In Python, "1234".replace(1234, "banana", 1) produces this: > > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > TypeError: replace() argument 1 must be str, not int > > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > TypeError: replace() argument 1 must be str, not int > > On Sunday, September 15, 2019 at 4:03:04 PM UTC-7, Dmitry Belyaev wrote: >> >> The error message is clear: >> BracketPush.check_brackets makes a call to >> https://hexdocs.pm/elixir/String.html#replace/4 and passes empty string >> "" as the last argument instead of a list. >> >> In dynamically typed languages we expect the caller to pass the correct >> data for the function to behave as documented. Imagine every function >> typechecked every argument to the deepest primitive types - the code would >> become an incomprehensible mess and performance would be terrible wth every >> function type checking the data in runtime which was already typechecked >> one level higher. >> >> On 16 September 2019 7:50:56 am AEST, Landon Schropp <[email protected]> >> wrote: >>> >>> Hello! >>> >>> I'm new to Elixir development, so I'm approaching the language with >>> fresh eyes. One of the issues I've run into is difficulty understand the >>> error messages from the standard library. For example, today I ran this >>> code: >>> >>> string >>> |> String.replace(string, ~r/[^\[\](){}]/, "") >>> |> IO.inspect(label: "\n") >>> |> String.codepoints >>> |> check_brackets([]) >>> >>> When I run this code through one of my test cases, I get the following >>> error: >>> >>> 1) test math expression (BracketPushTest) >>> test/bracket_push_test.exs:52 >>> ** (FunctionClauseError) no function clause matching in Keyword.get >>> /3 >>> The following arguments were given to Keyword.get/3: >>> >>> >>> # 1 >>> >>> "" >>> >>> >>> # 2 >>> :insert_replaced >>> >>> >>> # 3 >>> nil >>> >>> >>> Attempted function clauses (showing 1 out of 1): >>> >>> >>> def get(keywords, key, default) when is_list(keywords) and >>> is_atom(key) >>> >>> >>> code: assert BracketPush.check_brackets("(((185 + 223.85) * 15) - >>> 543)/2") == true >>> stacktrace: >>> (elixir) lib/keyword.ex:195: Keyword.get/3 >>> (elixir) lib/string.ex:1372: String.replace/4 >>> (bracket_push) lib/bracket_push.ex:15: BracketPush.check_brackets >>> /1 >>> test/bracket_push_test.exs:53: (test) >>> >>> test/bracket_push_test.exs:52 >>> ** (FunctionClauseError) no function clause matching in Keyword.get >>> /3 >>> The following arguments were given to Keyword.get/3: >>> >>> >>> # 1 >>> >>> "" >>> >>> >>> # 2 >>> :insert_replaced >>> >>> >>> # 3 >>> nil >>> >>> >>> Attempted function clauses (showing 1 out of 1): >>> >>> >>> def get(keywords, key, default) when is_list(keywords) and >>> is_atom(key) >>> >>> >>> code: assert BracketPush.check_brackets("(((185 + 223.85) * 15) - >>> 543)/2") == true >>> stacktrace: >>> (elixir) lib/keyword.ex:195: Keyword.get/3 >>> (elixir) lib/string.ex:1372: String.replace/4 >>> (bracket_push) lib/bracket_push.ex:15: BracketPush.check_brackets >>> /1 >>> >>> As a new language user, it's really hard to understand what I did wrong >>> when the error is thrown from inside the inner implementation of >>> String.replace/4. In order to debug this, I'd either have to look at >>> the inner implementation of String.replace/4 or attempt to fiddle with the >>> arguments. My preference would be for standard library functions to guard >>> their own interfaces, and throw specific and actionable messages back to >>> the developer. >>> >>> Thanks for taking the time to read! >>> >>> >> -- >> Kind regards, >> Dmitry Belyaev >> > -- > 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/52657dee-f2e3-44de-a430-0b8d72eeccad%40googlegroups.com > <https://groups.google.com/d/msgid/elixir-lang-core/52657dee-f2e3-44de-a430-0b8d72eeccad%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- 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/CAGnRm4LVm9tq4%2BQ1G3Gk0zTaaNiCySBy3XYYmhJHkj28DpDxXQ%40mail.gmail.com.
