Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Clojure say "no matching method" for an illegal argument?

Tags:

clojure

Correct usages of Character/isWhitespace include:

(Character/isWhitespace \a) => false
(Character/isWhitespace \ ) => true

However, my first attempt was this, and I find the error confusing.

(Character/isWhitespace "")
  =>  IllegalArgumentException No matching method found: isWhitespace
  => clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:80)

The IllegalArgument part makes sense, but why does it say "no matching method found"? Clearly the function does exist.

Clarification

The reason I'm asking this question is that I'm new to Clojure, and think I'm fundamentally misunderstanding something.

When I type (Character/isWhitespace \a), what I think I'm saying is: "I know that there's a Character namespace, and within that there's a function called isWhitespace, and I want to call that function and pass in \a".

On this mental model, my results above are confusing because it seems like Clojure is saying, "whenever you give me an argument type that this function doesn't accept, I'm going to pretend the function doesn't exist." Eg, "you're not allowed to blend bricks, so if you try, I'm going to give you a BlenderDoesntExist error." Which is weird.

Some answers seem to imply that

  • The name Character/isWhitespace is only part of what Clojure uses to look up the function, and the other part is the type of the argument. (I've done some more searching: is this maybe a multimethod?)
  • The method is being looked up on a Java class?

A great answer would clarify this process for me.

like image 496
Nathan Long Avatar asked Oct 25 '13 12:10

Nathan Long


2 Answers

tl;dr

The clojure compiler relies on reflection in order to find matching signatures for Java interop Class methods, and it's raising it's own exceptions when nothing is found.

In this case an IllegalArgumentException is correctly raised but noMethodReport error message is displayed which leads to confusion.

And this is the source code responsible for it on the clojure github repo.

The long version

First, Java interop parsing walkthrough.

When the clojure parser finds the . dot macro the HostExpr parser handles the parsing, which tries to decide whether second argument is a symbol or a Class.

If it's a class assumes it's a static method of that class being called, and parsing continues on StaticMethodExpr.

First thing inside parser is try to find the method by reflection on the class:

  List methods = Reflector.getMethods(c, args.count(), methodName, true);
  if(methods.isEmpty())
      throw new IllegalArgumentException("No matching method: " + methodName);

Which it properly finds and no exception is raised at that point

Then it adds parameters to the found methods:

  java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i);
  params.add(m.getParameterTypes());

After that tries to find the matching method signature index:

  methodidx = getMatchingParams(methodName, params, args, rets);

Which for this case returns '-1' and method stays null. And that's for the parsing stage.

Evaluation time...

Then when invokeStaticMethod is called, it calls getMethods on Reflector.java which properly find two matching signatures for 'isWhitespace'.

And finally the confusing message you're seeing, inside function:

 static Object invokeMatchingMethod(String methodName, List methods, Object target, Object[] args)

Found methods are tested for parameter matching trying to find a method with the proper signature:

 for(Iterator i = methods.iterator(); i.hasNext();)
   {
    m = (Method) i.next();
    Class[] params = m.getParameterTypes();
    if(isCongruent(params, args))

If no matching signature is found an exception is raised

if(m == null)
   throw new IllegalArgumentException(noMethodReport(methodName,target));

So the answer would be that the clojure compiler relies on reflection in order to find matching signatures for the methods, and it's raising it's own exceptions when nothing is found.

In this case an IllegalArgumentException is correctly raised but noMethodReport error message is displayed which leads to confusion.

like image 144
guilespi Avatar answered Sep 29 '22 08:09

guilespi


Character/isWhitespace is a Java method (a static method of the java.lang.Character class). See Java interop for examples of syntax that call Java methods.

Whereas regular Clojure functions are defined by their name alone, Java methods are defined by their signature which consist of their name and the number and types of their parameters.

The only variants of the isWhitespace method defined in the Character class are isWhitespace(char ch) and isWhitespace(int codepoint). So there's "no matching method" for calling isWhitespace with a string.

like image 27
Rörd Avatar answered Sep 29 '22 08:09

Rörd