How to catch any Javascript exception in Clojurescript? - error-handling

In my communication layer I have a need to be able to catch ANY javascript exception, log it down and proceed as I normally would do.
Current syntax for catching exceptions in Clojurescript dictates that I need to specify the type of the exception being caught.
I tried to use nil, js/Error, js/object in the catch form and it doesn't catch ANY javascript exception (which can have string as the type of the object).
I would appreciate any hints how this can be done natively in Clojurescript.

I found another possible answer in David Nolen "Light Table ClojureScript Tutorial"
;; Error Handling
;; ============================================================================
;; Error handling in ClojureScript is relatively straightforward and more or
;; less similar to what is offered in JavaScript.
;; You can construct an error like this.
(js/Error. "Oops")
;; You can throw an error like this.
(throw (js/Error. "Oops"))
;; You can catch an error like this.
(try
(throw (js/Error. "Oops"))
(catch js/Error e
e))
;; JavaScript unfortunately allows you to throw anything. You can handle
;; this in ClojureScript with the following.
(try
(throw (js/Error. "Oops"))
(catch :default e
e))

It looks like js/Object catches them all (tested on https://himera.herokuapp.com):
cljs.user> (try (throw (js/Error. "some error")) (catch js/Object e (str "Caught: " e)))
"Caught: Error: some error"
cljs.user> (try (throw "string error") (catch js/Object e (str "Caught: " e)))
"Caught: string error"
cljs.user> (try (js/eval "throw 'js error';") (catch js/Object e (str "Caught: " e)))
"Caught: js error"
One thing to watch out for is lazy sequences. If an error is thrown in a lazy sequence that part of the code might not be executed until after you've exited the try function. For example:
cljs.user> (try (map #(if (zero? %) (throw "some error")) [1]))
(nil)
cljs.user> (try (map #(if (zero? %) (throw "some error")) [0]))
; script fails with "Uncaught some error"
In that last case, map creates a lazy sequence and the try function returns it. Then, when the repl tries to print the sequence to the console, it's evaluated and the error gets thrown outside of the try expression.

I think I've just found the solution in this link
https://groups.google.com/forum/#!topic/clojure/QHaTwjD4zzU
I copy the contents here:
This solution was published by Herwig Hochleitner
try in clojurescript is actually a macro that uses the builtin try*
and adds type dispatch. So to catch everything, just use (try* ...
(catch e ...)). This maps directly to javascript's try.
And here is my implementation working now:
(defn is-dir? [the_dir]
(try*
(if-let [stat (.statSync fs the_dir )]
(.isDirectory stat)
false)
(catch e
(println "catching all exceptions, include js/exeptions")
false
)
)
)
I hope it helps you
Juan

Related

Error running timer: (void-variable message) in Emacs init.el

Why do I get Error running timer: (void-variable message)
in the function below in my `init.el - Emacs?
(defun cypher/cowsayx-sclock (in-minutes message)
(interactive "nSet the time from now - min.: \nsWhat: ")
(run-at-time (* in-minutes 60)
nil
(lambda ()
(message "%S" message)
(shell-command (format "xcowsay %s" (shell-quote-argument
message))))))
You need to turn on lexical-binding, for that message occurrence in the lambda not to be treated as a free variable. It's a lexical variable local to function cypher/cowsayx-sclock, but within the lambda it's free.
Otherwise, you need to instead substitute the value of variable message in the lambda expression, and use that as a list. Here's a backquoted expression that gives you that list with the message value substituted.
`(lambda ()
(message "%S" ',message)
(shell-command (format "xcowsay %s" (shell-quote-argument ',message)))
But this is less performant than using lexical-binding, which produces a closure for the lambda, encapsulating the value of message.
See the Elisp manual, node Using Lexical Binding.
You can, for example, just put this at the end of a comment line as the first line of your file:
-*- lexical-binding:t -*-
For example, if your code is in file foo.el then this could be its first line:
;;; foo.el --- Code that does foo things. -*- lexical-binding:t -*-

How does read-line work in Lisp when reaching eof?

Context:
I have a text file called fr.txt with 3 columns of text in it:
65 A #\A
97 a #\a
192 À #\latin_capital_letter_a_with_grave
224 à #\latin_small_letter_a_with_grave
etc...
I want to create a function to read the first (and eventually the third one too) column and write it into another text file called alphabet_code.txt.
So far I have this function:
(defun alphabets()
(setq source (open "fr.txt" :direction :input :if-does-not-exist :error))
(setq code (open "alphabet_code.txt" :direction :output :if-does-not-exist :create :if-exists :supersede))
(loop
(setq ligne (read-line source nil nil))
(cond
((equal ligne nil) (return))
(t (print (read-from-string ligne) code))
)
)
(close code)
(close source)
)
My problems:
I don't really understand how the parameters of read-line function. I have read this doc, but it's still very obscure to me. If someone would have very simple examples, that would help.
With the current code, I get this error: *** - read: input stream #<input string-input-stream> has reached its end even if I change the nil nil in (read-line source nil nil) to other values.
Thanks for your time!
Your questions
read-line optional arguments
read-line accepts 3 optional arguments:
eof-error-p: what to do on EOF (default: error)
eof-value: what to return instead of the error when you see EOF
recursive-p: are you calling it from your print-object method (forget about this for now)
E.g., when the stream is at EOF,
(read-line stream) will signal the end-of-file error
(read-line stream nil) will return nil
(read-line stream nil 42) will return 42.
Note that (read-line stream nil) is the same as (read-line stream nil nil) but people usually still pass the second optional argument explicitly.
eof-value of nil is fine for read-line because nil is not a string and read-line only returns strings.
Note also that in case of read the second optional argument is, traditionally, the stream itself: (read stream nil stream). It's quite convenient.
Error
You are getting the error from read-from-string, not read-line, because, apparently, you have an empty line in your file.
I know that because the error mentions string-input-stream, not file-stream.
Your code
Your code is correct functionally, but very wrong stylistically.
You should use with-open-file whenever possible.
You should not use print in code, it's a weird legacy function mostly for interactive use.
You can't create local variables with setq - use let or other equivalent forms (in this case, you never need let! :-)
Here is how I would re-write your function:
(defun alphabets (input-file output-file)
(with-open-stream (source input-file)
(with-open-stream (code output-file :direction :output :if-exists :supersede)
(loop for line = (read-line source nil nil)
as num = (parse-integer line :junk-allowed t)
while line do
(when num
(write num :stream code)
(write-char #\Newline code))))))
(alphabets "fr.txt" "alphabet_code.txt")
See the docs:
loop: for/as, while, do
write, write-char
parse-integer
Alternatively, instead of (when num ...) I could have use the corresponding loop conditional.
Also, instead of write+write-char I could have written (format code "~D~%" num).
Note that I do not pass those of your with-open-stream arguments that are identical to the defaults.
The defaults are set in stone, and the less code you have to write and your user has to read, the less is the chance of an error.

IntelliJ/Cursive unexpected issue

Sometimes I try to run a Clojure program in IntelliJ and I get the following error:
(.deleteOnExit (java.io.File. "/home/matan.bl/.IdeaIC15/system/tmp/form-init7886362698780123516.clj"))
(do (set! *warn-on-reflection* nil)
(set! *warn-on-reflection* nil)
nil
(clojure.core/doseq [namespace1387 (quote
(sparkling.serialization
sparkling.destructuring))]
(clojure.core/binding [clojure.core/*out* clojure.core/*err*]
(clojure.core/println "Compiling" namespace1387))
(try
(clojure.core/compile namespace1387)
(catch java.lang.Throwable
t__8048__auto__ (clojure.core/binding [clojure.core/*out*
clojure.core/*err*]
(clojure.core/println (.getMessage
t__8048__auto__)))
(throw t__8048__auto__))))
(do nil
(try
(clojure.core/require
(quote clojure.tools.nrepl.server))
(catch
java.lang.Throwable t__11819__auto__
(clojure.core/println "Error loading"
(clojure.core/str (quote clojure.tools.nrepl.server) ":")
(clojure.core/or (.getMessage t__11819__auto__)
(clojure.core/type
t__11819__auto__)))))
(try (clojure.core/require (quote complete.core))
(catch java.lang.Throwable t__11819__auto__
(clojure.core/println "Error loading"
(clojure.core/str
(quote
complete.core) ":")
(clojure.core/or (.getMessage t__11819__auto__)
(clojure.core/type t__11819__auto__)))))
nil)
(clojure.core/let
[server__11814__auto__ (clojure.tools.nrepl.server/start-server
:bind
"127.0.0.1" :port 0 :ack-port 42520 :handler
(clojure.tools.nrepl.server/default-handler))
port__11815__auto__ (:port server__11814__auto__)
repl-port-file__11816__auto__ (clojure.core/apply clojure.java.io/file
["/home/matan.bl/projects/af_sparkling-river"
".nrepl-port"])
legacy-repl-port__11817__auto__ (if (.exists (clojure.java.io/file
"/home/matan.bl/projects/af_sparkling- river/target"))
(clojure.java.io/file
"/home/matan.bl/projects/af_sparkling-river/target" "repl-port"))]
(clojure.core/when true
(clojure.core/println
"nREPL server started on port"
port__11815__auto__
"on host"
"127.0.0.1"
(clojure.core/str "- nrepl://" "127.0.0.1" ":" port__11815__auto__)))
(clojure.core/spit
(clojure.core/doto repl-port-file__11816__auto__ .deleteOnExit)
port__11815__auto__)
(clojure.core/when
legacy-repl-port__11817__auto__
(clojure.core/spit (clojure.core/doto
legacy-repl-port__11817__auto__
.deleteOnExit)
port__11815__auto__))
(clojure.core/deref (clojure.core/promise))))
Do you have any idea what this error means and how it can be solved?
I have seen errors like this when typing
(-main ....)
into the REPL in Cursive-clojure without first clicking
on the switch repl to namespace button. Also make sure you have clocked "load file" in the same menu.

How to correctly implement error handling?

I am calling a function fit_circle that may or may not call the MESSAGE procedure. fit_circle calls a function poly_fit that may call the MESSAGE procedure, but also occasionaly produces a math error (there is nothing I can do about this).
Is it possible to contruct an error handler that will take care of all of these scenarios without returning to the interactive prompt? I have tried the following:
FUNCTION arb_funct,circular_data
CATCH, ERROR
IF ERROR NE 0 THEN BEGIN
CATCH, /CANCEL
print, 'An Error Occured'
return, []
ENDIF
circle_fit_result = fit_circle(circular_data)
return, circle_fit_result
END
However the error handler never triggers. The documentation only seems to mention looking for error handlers defined in procedures.
Works for me. Here is my example code:
pro mg_error_demo_helper2
compile_opt strictarr
a = b
end
pro mg_error_demo_helper1
compile_opt strictarr
mg_error_demo_helper2
end
pro mg_error_demo
compile_opt strictarr
catch, error
if (error ne 0L) then begin
catch, /cancel
print, 'An error occurred'
return
endif
mg_error_demo_helper1
end
When I run it, I get:
IDL> mg_error_demo
% Compiled module: MG_ERROR_DEMO.
error happened
IDL has three different types of "catchable" errors:
"Normal" Errors: These are the most common type of error to handle. They are thrown when message is called. Use catch or on_error to deal with them.
IO Errors: These happen when a conversion, disk read, memory, or other "IO" error occurs. Usually, these will also be caught by catch and on_error. When they aren't, use on_ioerror to deal with them.
Math Errors: These happen when you divide by 0, do certain math with NaN or infinity, etc. Deal with them using check_math and !except.
Here's an example of using all three in one program:
function arb_funct, circular_data
; handle normal errors
catch, error
if error ne 0 then begin
catch, /cancel
print, 'A normal error occured: ' + !error_state.msg
return, []
endif
; handle IO errors
on_ioerror, arb_funct__ioerror
if 0B then begin
arb_funct__ioerror:
print, 'An IO error occured: ' + !error_state.msg
return, []
endif
; clear math errors from previous routines
math_err = check_math(/print)
previous_except = !except
!except = 0
; do stuff
circle_fit_result = fit_circle(circular_data)
; handle math errors
math_err = check_math()
!except = previous_except
if math_err ne 0 then begin
print, 'A math error occured: ' + strtrim(math_err, 1)
return, []
endif
; and finally return
return, circle_fit_result
end
Here is an example of to what I referred:
FUNCTION some_function
;;--------------------------------------------------------------------------
;; Error handling [put towards beginning of routine]
;;--------------------------------------------------------------------------
CATCH, error_status
IF (error_status NE 0) THEN BEGIN
;; Cancel error handler
CATCH, /CANCEL
;; Print error message
PRINT, 'Error index: ', error_status
PRINT, 'Error message: ', !ERROR_STATE.MSG
;; Define return variable or how to handle error
;; --> I usually use a formatted output consistent with the expected
;; output, but here I will just use 0 for example.
dumb = 0
;; Return defined variable
RETURN,dumb
ENDIF
;;--------------------------------------------------------------------------
;; Main body of routine
;;--------------------------------------------------------------------------
blah = 0
; blah
; blah
; blah
;;--------------------------------------------------------------------------
;; Return to user
;;--------------------------------------------------------------------------
RETURN,return_variable
END
Hope that helps...

syntax error in function in a module, OCaml

Hi my following code gives a "Syntax Error" and i have no idea why...
Any basic mistake you can see?
(* ajouter_itineraire: itineraire -> plan -> plan *)
let ajouter_itineraire (i: itineraire) (p: plan) = match p with
| Vide ->[i]
| Ilist l ->if itineraire_existe(i.num) then
Err("Itineraire deja present") else i::p.Ilist
Err is an exception taking a string as parameter.
Your code tries to return the exception as a value. I think you want to say raise (Err "Itineraire deja present"). Also p.Ilist looks wrong. Most likely this should be just l.