Why doen't this send pattern match? - rubocop

I have written a custom rubocop cop, the excerpts are like this:
def_node_matcher :is_foo_bar?, <<-PATTERN
(send (const nil :Foo) :bar)
PATTERN
def on_send(node)
puts "Match" if is_foo_bar?(node)
end
I'm trying the cop on the following code:
Foo.bar
Interestingly enough, node.to_s says the following, which exactly matches my pattern:
"(send
(const nil :Foo) :bar)"
But the node does not match. If I change the pattern to the following, it works though:
(send (...) :bar)
Why doesn't my original match work?
My versions:
ruby 2.6.2
ast 2.4.0
rubocop 0.77.0
parser 2.6.4.1

Some time back, we changed the matcher for nil to be nil?. This has the unfortunate side-effect that you can no longer copy-paste output from ruby-parse or node.to_s and instantly have a working matcher.
Just adding the additional question mark should make your pattern work again:
def_node_matcher :is_foo_bar?, <<-PATTERN
(send (const nil? :Foo) :bar)
PATTERN

Related

When do functions expecting maps silently return nil when passed a non-map argument?

I'm a bit surprised to see some functions silently returning nil (or the default value you pass) when you pass something that is not a map where a map is expected.
Take first the following doc and example, which works as I expected:
user> (doc dissoc)
-------------------------
clojure.core/dissoc
([map] [map key] [map key & ks])
dissoc[iate]. Returns a new map of the same (hashed/sorted) type,
that does not contain a mapping for key(s).
The first argument to dissoc must be a map as per the doc, so here's what happens:
user> (dissoc {:a 1 :b 2} :b)
{:a 1}
It works fine, I passed a map. Let's pass something that is not a map to that function whose doc says the first argument should be a map:
user> (dissoc 1 :b)
ClassCastException java.lang.Long cannot be cast to clojure.lang.IPersistentMap clojure.lang.RT.dissoc (RT.java:758)
Fair enough, an exception is thrown at runtime and the issue is clear enough, this is the behavior I expected.
Let's now take another function:
user> (doc get)
-------------------------
clojure.core/get
([map key] [map key not-found])
Returns the value mapped to key, not-found or nil if key not present.
The first argument to get must be a map as per the doc and here's what happens:
user> (get {:a 1} :a)
1
So far so good. Let's pass something that is not a map to that function whose doc says the first argument should be a map:
user> (get 42 :a)
nil
No exception. No nothing. Just a silent "failure".
How comes one function throws an exception when you pass something that is not a map and not the other, although both functions' docs clearly state that the first argument must be a map?
Is there a "rule" to know when, in Clojure, you'll get either exceptions or nil or should I just expect that kind of stuff to be inconsistent?
As a side-question: would Clojure and Clojure programs "break" if, say, get was modified to throw an exception instead of silently returning nil when you don't pass a map where you're supposed to?
An exception should probably be thrown for a get on non-associative arguments as in your example:
(contains? 42 :a)
;=> IllegalArgumentException
(get 42 :a)
;=> nil ??
This is an open issue in Clojure's Issue Tracker. As stated in the issue description, the current behavior of returning a nil is most likely a bug and can obscure programming errors.
get is intended to work on all associative data structures. The language implementors could have chosen to check if the argument was associative, or made a white-list of all associative things that it could check, but instead it has a few special cases defined in clojure.lang.RT, and will also work on anything that implements Map or IPersistentSet or ILookup.
the code is not too hard to grok
Now, as to why it would silently return nil on something that implements none of these interfaces, this is an instance of a persistent Clojure design strategy of silently returning nil rather than failing. This decision definitely has tradeoffs, but it is a pervasive way Clojure does things.

Find nodes with css-selectors:query

I'm having trouble getting css-selectors:query to work.
Totally didn't understand what args go in and in what order.
http://quickdocs.org/css-selectors/
Looking at the source code:
(defun query (inp &optional (trees buildnode:*document*))
"Given a css selector, attempt to find the matching nodes in the passed in
dom-trees (defaults to the document)"
(%query inp trees))
I don't know what inp stands for but by process of elimination assume it to mean a css selector string.
(defun get-page (url)
"Get STP(DOM alternative) representation of page"
(chtml:parse
(drakma:http-request url)
(cxml-stp:make-builder)))
(css-selectors:query "a" (get-page "http://lisp.org/")) ; Doesn't work
(css-selectors:query (get-page "http://lisp.org/") "a") ; Worth a try
Example usage will greatly help.
Edit: Quickload css-selectors-stp to get it working with STP.
I contacted the author and the documentation has been made more clear. Support for STP should have been merged in and complete but the author wrote this package for DOM and has never used STP. So it SHOULD work for STP but doesn't for whatever reason.
The following works:
(defun get-page (url)
"Get DOM representation of page"
(chtml:parse
(drakma:http-request url)
(cxml-dom:make-dom-builder)))
(css-selectors:query "a" (get-page "http://lisp.org/")) ; Success!

Embedded ECL Lisp error handling fetch default error string and possibly line number

Please see #7755661 first. I am using ECL and basically want to execute some code, trap any kind of condition that may occur and then continue execution, without prompting or entering the debugger. This is easy to achieve with the following handler-case macro:
(handler-case
(load "code.lisp") ; this may raise a condition
(error (condition)
(print condition))) ; this prints sth like #<a UNBOUND-VARIABLE>
My only problem is that I cannot find a generic way to print a more meaningful error for the user. Indeed my application is an HTTP server and the output goes to a web page. code.lisp is written by the user and it can raise any kind of condition, I do now want to list them all in my code. I would just like to print the same error message I see on the REPL when I do not use handler-case, but in the HTML page, e.g. for an "unbound variable" error, a string like "The variable VAR is unbound".
By inspecting a condition object of type UNBOUND-VARIABLE I see it has two slots: SI:REPORT-FUNCTION, which is a compiled function and SI:NAME, set to the name of the variable in this case. I guess SI:REPORT-FUNCTION could be what I need to invoke but how can I call it? If I try:
(handler-case foo (error (condition) (SI::REPORT-FUNCTION condition)))
it tells me that SI:REPORT-FUNCTION is undefined. SI or SYS in ECL is a package for functions and variables internal to the implementation, but I don't worry if my code is not portable, as long as it works.
BTW in other kinds of condition objects there are also other apparently useful slots for my purpose, named SI:FORMAT-CONTROL and SI:FORMAT-ARGUMENT, but I cannot access any of them from my code too.
I was looking for somethink alike to the getMessage() method of Java exception objects in Lisp, but none of my sources ever mentions something like that.
Moreover, is there any hope to be able to get the line number in code.lisp where the error occurred too? Without that it would be difficult for the user to locate the problem in his code.lisp source file. I would really want to provide this information and stopping at the first error is acceptable for me.
In Common Lisp when print escaping is disabled, the error message is printed.
CL-USER > (handler-case
a
(error (condition)
(write condition :escape nil)))
The variable A is unbound.
#<UNBOUND-VARIABLE 4020059743>
Note that PRINT binds *print-escape* to T.
Using PRINC works - it binds *print-escape* to NIL.
CL-USER > (handler-case
a
(error (condition)
(princ condition)))
The variable A is unbound.
#<UNBOUND-VARIABLE 4020175C0B>
This is described in CLHS 9.1.3 Printing Conditions.
Also note, when you have an object, which has a slot and the value of this slot is a function, then you need to get the slot value using the function SLOT-VALUE and then use FUNCALL or APPLY and call the function with the correct arguments.
If you have a condition of type simple-condition then it has a format-control and a format-argument information. This is described with an example how to use it for FORMAT in CLHS Function SIMPLE-CONDITION-FORMAT-CONTROL, SIMPLE-CONDITION-FORMAT-ARGUMENTS
My answer below is based on one I already gave at the ECL mailing list. Actually I would claim that this is not an embedding problem, but a Lisp one. You want to get some information at the file position of the form which caused the error. This is not attached to a condition because conditions happen independently of whether the form evaluated was interpreted, compiled or part of a function that is already installed in the Lisp image. In other words, it is up to you to know the position of the file which is being read and do some wrapping that adds the information.
The following is nonstandard and prone to change: ECL helps you by defining a variable ext::source-location when LOAD is used on a source file. This variable contains a CONS that should NEVER be changed or stored by the user, but you can get the file as (CAR EXT:*SOURCE-LOCATION*) and the file position as (CDR EXT:*SOURCE-LOCATION*). The plan is then to embed your LOAD form inside a HANDLER-BIND
(defparameter *error-message* nil)
(defparameter *error-tag* (cons))
(defun capture-error (condition)
(setf *error*
(format nil "At character ~S in file ~S an error was found:~%~A"
(cdr ext:*source-location*)
(car ext:*source-location*)
condition)))
(throw *error-tag* *error-message*))
(defun safely-load (file)
(handler-bind ((serious-condition #'capture-error))
(catch *error-tag*
(load file)
nil)))
(SAFELY-LOAD "myfile.lisp") will return either NIL or the formatted error.
In any case I strongly believe that relying on LOAD for this is doomed to fail. You should create your own version of LOAD, starting from this
(defun my-load (userfile)
(with-open-file (stream userfile :direction :input :external-format ....whateverformat...)
(loop for form = (read stream nil nil nil)
while form
do (eval-form-with-error-catching form))))
where EVAL-FORM-.... implements something like the code above. This function can be made more sophisticated and you may keep track of file positions, line numbers, etc. Your code will also be more portable this way.
So please, read the ANSI Spec and learn the language. The fact that you did not know how to print readably a condition and instead tried to play with ECL internals shows that you might face further problems in the future, trying to go with non-portable solutions (hidden slot names, report functions, etc) instead of first trying the standard way.

Haskell: List Comprehension to Combinatory

Inspired by this article. I was playing with translating functions from list comprehension to combinatory style. I found something interesting.
-- Example 1: List Comprehension
*Main> [x|(x:_)<-["hi","hello",""]]
"hh"
-- Example 2: Combinatory
*Main> map head ["hi","hello",""]
"hh*** Exception: Prelude.head: empty list
-- Example 3: List Comprehension (translated from Example 2)
*Main> [head xs|xs<-["hi","hello",""]]
"hh*** Exception: Prelude.head: empty list
It seems strange that example 1 does not throw an exception, because (x:_) pattern matches one of the definitions of head. Is there an implied filter (not . null) when using list comprehensions?
See the section on list comprehensions in the Haskell report. So basically
[x|(x:_)<-["hi","hello",""]]
is translated as
let ok (x:_) = [ x ]
ok _ = [ ]
in concatMap ok ["hi","hello",""]
P.S. Since list comprehensions can be translated into do expressions, a similar thing happens with do expressions, as detailed in the section on do expressions. So the following will also produce the same result:
do (x:_)<-["hi","hello",""]
return x
Pattern match failures are handled specially in list comprehensions. In case the pattern fails to match, the element is dropped. Hence you just get "hh" but nothing for the third list element, since the element doesn't match the pattern.
This is due to the definition of the fail function which is called by the list comprehension in case a pattern fails to match some element:
fail _ = []
The correct parts of this answer are courtesy of kmc of #haskell fame. All the errors are mine, don't blame him.
Yes. When you qualify a list comprehension by pattern matching, the values which don't match are filtered out, getting rid of the empty list in your Example 1. In Example 3, the empty list matches the pattern xs so is not filtered, then head xs fails. The point of pattern matching is the safe combination of constructor discrimination with component selection!
You can achieve the same dubious effect with an irrefutable pattern, lazily performing the selection without the discrimination.
Prelude> [x|(~(x:_))<-["hi","hello",""]]
"hh*** Exception: <interactive>:1:0-30: Irrefutable pattern failed for pattern (x : _)
List comprehensions neatly package uses of map, concat, and thus filter.

find about about the type of function parameters

can i somehow find all functions/macros that take a specific type of parameter ?
for example, what function accepts a Namespace object as parameter ?
(this is because i can create a namespace and store it in a var, but i don't know where i could use that var; what function might i pass that var to ?)
here is some code:
user=> (def working-namespace (create-ns 'my-namespace))
#'user/working-namespace
;i created a namspace and want to use it later
user=> (class working-namespace)
clojure.lang.Namespace
; out of curiosity i found out that "working-namespace" is a Namespace object
user=> (ns working-namespace)
nil
working-namespace=>
; but how do i switch to it ? this didn't do what i wanted...
user=> (refer working-namespace)
java.lang.ClassCastException: clojure.lang.Namespace cannot be cast to clojure.lang.Symbol (NO_SOURCE_FILE:0)
; this did not work either as my expectations
user=> (the-ns working-namespace)
#<Namespace my-namespace>
user=> (class (the-ns working-namespace))
clojure.lang.Namespace
; great, this gave me the same thing, a Namespace
hence the question: how do i use it dynamically (that's why i had put my namespace into a var) ? how do i get something useful for me from a var that points to a namespace ?
i can try look around for functions that make use of a Namespace object or that convert it to something else. i did and only found "intern". searching by hand not seems not that promising
what if i have this problem a million time ? is there an automated way to get me what i'm looking for without having to ask around each time ?
In Clojure 1.2 and previous function arguments dont have types. every function argument is an object. So the question really becomes "how do i find functions that will cast the object I pass them into this type. so searching for type hints will find some of them, though it wont get you everything. I wish it where more possible to answer this in general.
starting with 1.3 (current dev branch 9/2010) function paramerters and return types can have a defined type and will be passed/returned as that type instead of being cast to object and then cast on the other side. This drops one of the zeros from the exacution time of numerical functions with the important limitation that it only works for :static functions and only with direct calls (ie: not through map/reduce/filter/etc.) There is not a lot published on this change yet though it has the important breaking change that integers are no longer boxed by default and integer (actually Long) overflow throws an exception. you can read more here
(defn ^:static fib ^long [^long n]
(if (<= n 1)
1
(+ (fib (dec n)) (fib (- n 2)))))
so after 1.3 is released and widely adopted you will see code with more commonly defined types because they will offer a big speed benefit and then you will be able to find more functions by argument type though still not all of them.
At the same lecture where I learned about function argument types, Rich mentioned plans in the distant Clojure future (after 'Clojure in Clojure') about better support for exposing the compiler internals to tools such as IDEs. So there is hope that someday you will get a real answer to this question.
Dynamic languages make this slightly more difficult in practice and a lot harder in theory.
You already got a good answer from Arthur, so I'll only answer the "how do i get something useful for me from a var that points to a namespace ?". From (doc ns), note that it says unevaluated:
user=> (doc ns)
-------------------------
clojure.core/ns
([name docstring? attr-map? references*])
Macro
Sets *ns* to the namespace named by name (unevaluated), creating it
Now there's something you could do with in-ns if you want (the whole namespace object -> string -> symbol conversion is probably stupid, but enough to illustrate my point):
user=> (in-ns (symbol (str working-namespace)))
#<Namespace my-namespace>
my-namespace=>
I don't think you can do that without a lot of hackery in a dynamic language. If you want to now what function that take namespaces look at the documentation of the namespace stuff.
For example cleaning namespaces or reload them.
You wrote:
user=> (ns working-namespace)
nil
working-namespace=>
; but how do i switch to it ? this didn't do what i wanted...
But you did switch to the working-namespace namespace (that's why the prompt changed), so I'm not clear as to what "you wanted".
As I noted earlier, you need to present the ultimate problem are you trying to solve. It's entirely likely that messing with namespace objects won't be the solution.