What is humanize in Malli? - functional-programming

I do not understand this concept. malli.error/humanize
I tried following up with the github link of Malli.
https://github.com/metosin/malli
Does humanize refer to the error statements that we can customize & add like in Java? Or does it mean that errors are printed in a more readable way?
And this is the code.
(require '[malli.error :as me])
(-> Address
(m/explain
{:id "Lillan"
:tags #{:artesan "coffee" :garden}
:address {:street "Ahlmanintie 29"
:zip 33100
:lonlat [61.4858322, nil]}})
(me/humanize))
;{:tags #{["should be a keyword"]}
; :address {:city ["missing required key"]
; :lonlat [nil ["should be a double"]]}}

"Does humanize refer to the error statements that we can customize & add like in Java? Or does it mean that errors are printed in a more readable way?"
So both actually. We can customize it too.
And errors are printed in a more human-readable way.

Related

How to set iolib timeout

I'm using IOLIB with this code to resolve a hostname:
(sockets:address-to-string (sockets:lookup-hostname name))
I works, but the functions does not take any timeout paramenter, and i cannot figure out how to set these using socket options.
Unfortunately this is not easy to find (in particular, this is not documented), but following the chain of calls from lookup-hostname (M-. in Emacs), you can see your code eventually calls dns-query:
(defun dns-query (name &key (type :a) (search *dns-search-domain*)
(nameservers *dns-nameservers*) decode
(repeat *dns-repeat*) (timeout *dns-timeout*))
...)
The timeout argument defaults to a special variable iolib/sockets::*dns-timeout*, which is globally bound to 10. You then only need to bind it around your code to set a different timeout:
(let ((iolib/sockets::*dns-timeout* 1))
...)
The variable is not exported, but dns-query is, maybe it is better to call that function directly.
CL-USER> (iolib:dns-query "http://example.com" :timeout 0.0001)
NIL
CL-USER> (iolib:dns-query "http://example.com" :timeout 1)
#<DNS RESPONSE Id: 61273, Question: #(#<"http://example.com." A IN>) Flags: :OP/S :RD :RA :NAME-ERROR, Sections: QD(1) AN(0) NS(1) AD(0)>

How to use Clozure CL on IPv6 only network?

I've tried to replace SBCL with Clozure CL when working in IPv6 only network, but encountered an error like that:
MIGRATIONS> (ignore-errors (ccl:make-socket :remote-host "ya.ru" :remote-port 443))
NIL
#<CCL:NO-APPLICABLE-METHOD-EXISTS #x302005215E5D>
MIGRATIONS> (ignore-errors (ccl:make-socket :remote-host "ya.ru" :remote-port 443 :address-family :internet))
NIL
#<CCL:NO-APPLICABLE-METHOD-EXISTS #x3020052549AD>
MIGRATIONS> (ignore-errors (ccl:make-socket :remote-host "ya.ru" :remote-port 443 :address-family :internet6))
#<BASIC-TCP-STREAM ISO-8859-1 (SOCKET/16) #x3020051D4A9D>
The problem is that many libraries when using CCL:MAKE-TCP-SOCKET don't specify address-family or specify an :internet.
Is there is a way to patch ccl:make-socket at runtime to override this setting?
Advise a function
Several implementations of Common Lisp allow advising (-> patching) of normal functions. Advising is a non-standard feature and different implementations provide it in slightly different ways. A related mechanism is standardized for CLOS generic functions with :before, :after and :around methods.
The purpose is to add one or more patches to a function, after it has been defined and without altering the original source code.
Typically this requires that the function call to this function is not inlined.
The macro ADVISE in Clozure Common Lisp
Patching functions in Clozure CL can be done with the macro ADVISE. See the documentation for advising.
Let's say we have a function FOOBAR:
? (defun foobar (a b &key c (d :foobar)) (list a b c d))
FOOBAR
FOOBAR gets called inside TEST:
? (defun test (a) (foobar a 20 :c 30))
TEST
? (test 10)
(10 20 30 :FOOBAR)
We now want to patch FOOBAR such that named arg :D gets called with a different value.
We change the arglist to insert the new named argument after the two required args:
? (advise foobar (let ((arglist (list* (first arglist)
(second arglist)
:d :ipv6
(cddr arglist))))
(:do-it)) ; calling the original function
:when :around ; advise around it
:name :ipv6) ; the name of this advise
#<Compiled-function (CCL::ADVISED 'FOOBAR) (Non-Global) #x3020010D1CCF>
Now we can call our TEST function and it will call the advised function FOOBAR.
? (test 10)
(10 20 30 :IPV6)
Advise for CCL:MAKE-SOCKET
You could write a similar advise for CCL:MAKE-SOCKET.
Untested:
(advise ccl:make-socket (let ((arglist (list* :address-family
:internet6
arglist)))
(:do-it))
:when :around
:name :internet6)
This can be done!
First make a copy of the original make-socket
(IN-PACKAGE :ccl)
(DEFPARAMETER original-make-socket #'make-socket)
Then redefine make-socket. Note: You will have to provide the full spec for all keyword parameters. As it is, I've used only the ones from your question for demonstration.
(defun make-socket (&key (remote-host "defau.lt")
(remote-port 443)
(address-family :internet6))
(declare (ignore address-family))
(format t "Calling new make-socket with address-family as internet6!")
(funcall original-make-socket
:remote-host remote-host
:remote-port remote-port
:address-family :internet6))
This will signal a continuable error.
Type :go at the repl to continue.
This will successfully patch make-socket.
Now any calls to make-socket will be to the new definition. Try:
(IN-PACKAGE :cl-user)
(ccl:make-socket :remote-host "ya.ru" :remote-port 443 :address-family :IRRELEVANT)
Another way to do it, would be to override the global variable *warn-if-redefine-kernel* before redefining make-socket.
(setf *warn-if-redefine-kernel* nil)
This will avoid the continuable error signal, and straight patch the kernel function.

Writing a JavaFX project in Clojure

I'm trying to understand how to properly setup JavaFX to work with a Clojure project. By reading various sources this is what I've come up with:
This is project.clj:
(defproject cljfx "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.8.0"]]
:resource-paths ["lib/jfxrt.jar"]
:main ^:skip-aot cljfx.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
I don't know if I should use :resource-paths or add JavaFX to the classpath via the :dependencies vector...
This is core.clj:
I've basically translated to Clojure an example from this tutorial:
http://docs.oracle.com/javafx/2/get_started/hello_world.htm
(ns cljfx.core
(:gen-class
:extends javafx.application.Application)
(:import
[javafx.application Application]
[javafx.stage Stage]
[javafx.scene Scene]
[javafx.scene.control Button]
[javafx.scene.layout StackPane]
[javafx.event ActionEvent EventHandler]))
(defn -main [& args]
(Application/launch cljfx.core args))
(defn button [text]
(doto (Button.)
(.setText (str "Say " text))
(.setOnAction (proxy [EventHandler] []
(handle [event]
(println text))))))
(defn -start [primaryStage]
(let [root (doto (StackPane.)
(-> (.getChildren)
(.add (button "Hello World!"))))]
(doto primaryStage
(.setScene (Scene. root 300 250))
(.show))))
This doesn't compile, and I don't know what I'm doing wrong... can you help me?
Here is the error:
http://pastebin.com/sYeK7MJd
There may be other problems, but the root problem in the pastebin log is:
Caused by: clojure.lang.ArityException: Wrong number of args (2) passed to: core/-start
When using gen-class and providing method implementations, every method needs to take the instance itself as the first parameter. The convention is to use "this":
(defn -start [this primaryStage]
Try that, and ensure that local instance method calls are applied to "this".

Unpredictable behaviour with drakma-async and cl-async

I'm trying to use drakma-async in my small project. But I just can't understand what's happening. (I use emacs + slime + ccl). I need to get data with http(s) and parse it in a callback. I assume I can get wrong data that cannot be parsed, so I want to make a retry. But when I tried to make some tests I just can't understand what's happening...
(defun my-callback (data)
(prin1 data)
(restart-case
(error "Some error parsing data...")
(just-continue () (prin1 "Continue..."))))
(defun simple-test ()
(let ((future (asf:make-future)))
(as:delay #'(lambda () (asf:finish future "Some data")) :time 2)
(prin1 (asf:future-finished-p future))
(asf:attach future #'my-callback)))
(defun drakma-test ()
(asf:alet ((response (das:http-request "http://www.google.com")))
;(prin1 (asf:future-finished-p response))
(asf:attach response #'my-callback)))
(defun drakma-test-let ()
(let ((response (das:http-request "http://www.google.com")))
;(prin1 (asf:future-finished-p response))
(asf:attach response #'my-callback)))
(defun run-test (test)
(as:start-event-loop test))
1) So I will that's what I have with my simple example (that's what I've planned)
? (run-test #'simple-test)
NIL"Some data" ;I get debugger here with simple-error and choose my restart
Invoking restart: #<RESTART JUST-CONTINUE #x7F0578EC20AD>
"Continue..."
1
2) Here what I get in second test:
? (run-test #'drakma-test)
"<A LOT OF HTML>
"
1
Where are my debugger and my restart?
3) Uncomment the ;(prin1 (asf:future...)) line in drakma-test
? (run-test #'drakma-test)
1
No finished/unfinished bool, No Data is not printed, I don't get a restart, I just get 1 as result.
4) I assume if i write (let ((reponse (das:http-request "http://www.google.com"))) ... )
instad of (asf:alet ...) the response will contain not future object, but will block until the request will be finished and the response will contain the data.
? (run-test #'drakma-test-let)
1
5) Uncomment the ;(prin1 (asf:future...)) line in drakma-test-let
? (run-test #'drakma-test-let)
NIL ;future is not finished
1
Data is not printed, just that is not finished and the result of run-test.
I've run tests for cl-async and they all passed except the ipv6 test. So I just don't know where to start to understand whats happening... Why I get no debugger and restart in 2nd test? Why nothing happens in 3rd test (it's the same as 2nd, but with prin1). Why nothing happens in 5th and 5th tests?
P.S. Don't have enough reputation to create drakma-async or cl-async tags for this libraries. I know that drakma-async is built over drakma so I put this tag.
Thanks for m-n's comment that made the situation clearer and explained shortly the situation.
I made some examples and want to show what happens in each case:
Example:
(defun my-callback (&rest data)
(format t "Echo from callback: ~A~%" data)
(restart-case
(error "Some error parsing data...")
(just-continue () (prin1 "Continue..."))))
(defun my-errback (e)
(format t "Echo from errback: ~A~%" e))
(defun make-example-future ()
(let ((future (asf:make-future))) ;creating future
(as:delay #'(lambda () ;finishing future in 2 seconds
(asf:future-handler-case ;wrapping asf:finish
(asf:finish future
"Result data")
(t (e) (asf:signal-error future e)))) ;signal future an error
:time 2)
future))
(defun simple-test-2 ()
(let ((future (make-example-future)))
(format t "Is future?: ~A~%Finished?: ~A~%"
(asf:futurep future) (asf:future-finished-p future))
(asf:alet ((result future))
(asf:attach-errback future #'my-errback)
(format t "Finished? ~A~%" (asf:future-finished-p future))
(asf:future-finished-p result)
(asf:attach result #'my-callback))))
And here is what's happening:
? (as:start-event-loop #'simple-test-2)
Is future?: T
Finished?: NIL
;<here we have a 2 sec pause>
Finished? T
Echo from errback: There is no applicable method for the generic function:
#<STANDARD-GENERIC-FUNCTION CL-ASYNC-FUTURE:FUTURE-FINISHED-P #x302001B67A8F>
when called with arguments:
("Result data")
A) asf:alet wait for result and bind the result value to the variable.
So I was wrong thinking that asf:alet bind a future.
B) In make-example-future we wrap asf:finish with asf:future-handler-case
and use asf:signal-error to send error to future.
That means that error is handled and the errback will be called.
Even if the callback is attached later in the code.
Moreover, the error with (asf:future-finished-p result)
was handled with future-handler-case because it was wrapped in asf:alet (At least I think so).
C) Comment the (asf:future-finished-p result) and the result is
Is future?: T
Finished?: NIL
Finished? T
Echo from callback: (Result data) ;here is my data
Echo from errback: Some error parsing data... ;;here is my error
1
In drakma-async there is similar future-handler-case wrapper that wraps asf:finish.
So this explains the #2 test result. I got the data and asf:alet returned me the string. The error from callback was passed to errback, which I didn't have.
Moreover. In drakma-test using only asf:alet I just can't attach errback because I don't have access to future. I need to call http-request in let, not in alet.
Also this explains the result of the #3 test: I got error in (future-finished-p) which was sent to errback.
If we look at the result of #4 and #5 test with new my-callback: It can be seen, that
cl-async try to call my callback with all values the drakma returned. There are 7 of them (the values that drakma:http-request return).
So I tried to attach wrong number of arguments callback and my #4 and #5 tests were signalling an error that was simply handled by that future-hander-case and send it to errback.
Result:
Anyway, it seems impossible to use restarts with drakma-async without removing that future-handler-case because it send error to errback, but lose all restarts.
Hope this post helps if somebody fill face up with my question.

can't use Racket's better-monads library

I'm trying to make use of Racket's better-monads library.
I have the following program :
(module bmonads racket
(provide add)
(require functional/better-monads)
(define (add)
(mlet* ((x 10)
(y 11))
(+ x y))))
When I try to load this file into the REPL(geiser), I get the following error message :
Welcome to Racket v5.3.4.
racket#> (require (file "bmonads.rkt"))
bmonads.rkt:4:11: functional/better-monads: standard-module-name-resolver: collection not found
collection: "functional"
in collection directories:
/home/me/.racket/5.3.4/collects
/usr/share/racket/collects
/home/me/.emacs.d/geiser-0.5/scheme/racket/
in: functional/better-monads
context...:
standard-module-name-resolver
standard-module-name-resolver
/usr/share/racket/collects/racket/private/misc.rkt:87:7
racket#>
Because better-monads is part of the PLT package functional.plt, you will need to load it via PlaneT.
(require (planet "better-monads.rkt" ("toups" "functional.plt" 1 1)))
Specific documentation for loading the library: http://planet.racket-lang.org/display.ss?package=functional.plt&owner=toups
General documentation on PlaneT: http://docs.racket-lang.org/planet/Using_PLaneT.html?q=planeT&q=monand

Resources