Where is cons defined in cljs? - clojurescript

I'm having trouble finding where cons has been defined in Clojurescript as it is not there in the ISeq protocol.
https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L616
Is it a compile time or runtime thing?

https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L3297
(defn cons
"Returns a new seq where x is the first element and coll is the rest."
[x coll]
(cond
(nil? coll) (List. nil x nil 1 nil)
(implements? ISeq coll) (Cons. nil x coll nil)
:default (Cons. nil x (seq coll) nil)))
https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L3237
(deftype Cons [meta first rest ^:mutable __hash]
ASeq
ISeq
(-first [coll] first)
(-rest [coll] (if (nil? rest) () rest)))

Related

How to pass a symbol to a function to create a function in clojure

As a minimal example of what I want to do:
(defn mkfn [func]
(fn func [a] (print "I am a function")))
(mkfn 'x) ; => #function[user/mkfn/func--10871]
(type x)
(x)
The last two both result in:
Syntax error compiling at (conjure-log-12628.cljc:1:1).
Unable to resolve symbol: x in this context
I'm not sure why this doesn't work since fn takes symbols as input and 'x is a symbol. I'm also not sure how to accomplish this task.
For that matter:
user=> (def (eval 'y) 3)
Syntax error compiling def at (conjure-log-12628.cljc:1:1).
user=> (def 'y 3)
Syntax error compiling def at (conjure-log-12628.cljc:1:1).
First argument to def must be a Symbol
First argument to def must be a Symbol
user=> (type 'y)
clojure.lang.Symbol
Other things that don't work:
(defn mkfn [func]
(fn (sympol func) [a] (print "i am a function")))
(symbol "y") ; => y ; a symbol
(def (symbol "y") 3) ; => an err
You will probably need a macro. It seems that you want to call that function by the provided name, so you also have to replace fn with defn.
And you have to be careful about a number of arguments, because function x with argument vector [a] must be called with one argument, and not like (x).
(defmacro mkfn [func]
`(defn ~func [~'a]
(print "I am a function")))
(mkfn x)
=> #'user/x
(x 1)
I am a function=> nil
There is also other way, using intern, so you can completely avoid writing macros:
(intern *ns* 'x (fn [a] (print "I am a function")))
=> #object...
(x 1)
I am a function=> nil
Example with intern:
(defn mkfn [func]
(intern *ns* func (fn [a] (print "I am a function"))))
=> #'user/mkfn
(mkfn 'y)
=> #'user/y
(y 1)
I am a function=> nil
As for your errors, def is a special form, so it has different evaluation rules. It doesn't evaluate the first argument, which has to be a symbol- and (unevaluated) (eval 'y), 'y or (symbol "y") aren't symbols, while y is.
You gonna need a macro for that since you need code writing code.
(defmacro mkfn [func]
`(fn ~func [~'a] ...))
There 2 ways of doing it, either function plus eval or macro. If you really want to programatically create a new function with your chosen name, the macro solution is the way to go.
The function + eval solution is instructive, but you'll have to either quote the function name when you call it (via a 2nd eval) or save the created function in another variable when you create it.
If you are interested in writing macros, please see this other question first: How do I write a Clojure threading macro?
For the function + eval, we can start with my favorite template project and add the following:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn maker-eval
[fn-sym]
(let [ll (list 'fn 'anon-fn [] (str "I am " fn-sym))]
(spyx :eval ll)
(eval ll)))
(verify
(let [anon-fn-1 (maker-eval 'aaa)]
(is= "I am aaa" (anon-fn-1))) ; need the temp local variable
(let [anon-fn-2 (maker-eval 'bbb)]
(is= "I am bbb" (anon-fn-2))) ; need the temp local variable
)
and we can see the creation and use of the function, along with printed output:
:eval ll => (fn anon-fn [] "I am aaa")
:eval ll => (fn anon-fn [] "I am bbb")
For the macro version, we type
(defn maker-macro-impl
[fn-sym]
(let [ll `(defn ~fn-sym [] (str "I am " (str (quote ~fn-sym))))]
(spyx :macro ll)
ll))
(defmacro maker-macro
[fn-sym] (maker-macro-impl fn-sym))
(verify
(let [anon-fn-3 (maker-macro-impl 'ccc)]
(is= anon-fn-3 (quote
(clojure.core/defn ccc [] (clojure.core/str "I am " (clojure.core/str (quote ccc)))))))
(maker-macro ddd)
(is= (ddd) "I am ddd"))
and see printed:
:macro ll => (clojure.core/defn ccc [] (clojure.core/str "I am " (clojure.core/str (quote ccc))))
Note that the local variable anon-fn-3 was only used to test the maker-macro-impl function, but was not needed to call the newly-created function ddd
at the end of the unit test.

Concatenate lists in Clojure Without Using concat

I am new to clojure, and am trying to understand how to concatenate lists without using the concat function.
(defn append [list1, list2]
(cond
(nil? list1) list2
:else (cons (first list1)
(append (rest list1) (list2)))))
When I run the above code, I get:
ClassCastException clojure.lang.PersistentList cannot be cast
to clojure.lang.IFn user/append
Reading from previous stackoverflow questions, it is because the cond expression is in double parentheses. I don't believe that I do.
Why am I still getting this error?
In Clojure, parentheses mean "function call". Thus the expression:
(list2)
means call a function that the list2 variable points at. This is the source of the error and the message clojure.lang.PersistentList cannot be cast to clojure.lang.IFn
Notes:
When a cond has only 2 branches, it is usually clearer to just a plain if expression.
Clojure code ignores commas, so they are normally never included in source code.
Consider carefully the difference between rest and next:
(rest []) => ()
(next []) => nil
You may wish to carefully study the Clojure CheatSheet and other documention listed here.
(defn append
[l1 l2 & [acc]]
(cond (and (empty? l1) (empty? l2)) (reverse acc)
(empty? l1) (append l1 (rest l2) (cons (first l2) acc))
:else (append (rest l1) l2 (cons (first l1) acc))))
or:
(defn append
([l1 l2]
(append l1 l2 '()))
([l1 l2 acc]
(cond (and (empty? l1) (empty? l2)) (reverse acc)
(empty? l1) (append l1 (rest l2) (cons (first l2) acc))
:else (append (rest l1) l2 (cons (first l1) acc)))))
;; or using vectors and conj
(defn vec-append
[v1 v2]
(cond (empty? v2) v1
:else (vec-append (conj v1 (first v2)) (rest v2))))
(defn append
[l1 l2]
(seq (vec-append (vec l1) (vec l2))))

Call a function while in a loop (Common Lisp)

I am making a console Lisp survival game and I am trying to add a function where until a = b, show "." every second. Then, when a = b, set a "hurt" variable to true, and if/when that variable is true, subtract "health" by 1 until the "use-medkit" function is invoked by the user and the "hurt" variable is set false and you exit both loops.
The problem I am having is when I am prompted to use the "use-medkit" function and I type it in, it doesn't evaluate anything that I input and keeps subtracting 1 from "health". How can I call a user-inputted function while a loop is running?
Here is my code:
(setq a (random 11)) ; Random from 0 - 10
(setq b (random 11)) ; ^^^^^^^^^^^^^^^^^^
(setq hurt 0)
(setq repair 0)
(setq health 999)
(defun use-medkit ()
(setq repair 1))
(defun get-hurt ()
(loop
(progn
(setq a (random 11))
(setq b (random 11))
(progn
(princ ".")
(sleep 1)))
(if (eq a b) (progn
(setq hurt 1)
(when (eq hurt 1) (progn
(format t "~%You are hurt!~%You will lose 1 hp every 10 seconds~%~%Type use-medkit to stop the bleeding~%")
(loop
(progn
(- 1 health)
(sleep 10))
;(format t "health: ~A~%" health)
(when (eq repair 1) (progn
(return "You stopped the bleeding") (setq hurt 0) (setq repair 0))))))))))
So a program can’t do two things at once. In particular if you’re busy printing dots, sleeping and subtracting 1 from 999 then you won’t pause to see if there’s another command coming.
Unfortunately solving this problem is hard. The nicest solution in a terminal would probably use something like ncurses. Additionally there is no standard way to control input buffering. In lieu of that, here is a simple way you can do a bit of concurrency and some prompts. You might want to use a proper async library instead.
(defun maybe-read (input-stream recording-stream)
(when (listen input-stream)
(let ((char (read-char input-stream)))
(if (char= char #\Newline)
t
(progn (write-char char recording-stream) (maybe-read))))))
(defun make-partial-reader (input-stream)
(list input-stream (make-string-output-stream)))
(defun partial-read (reader)
(when (apply #'maybe-read reader)
(get-output-stream-string (second reader))))
(defun how-long-until (time)
(let ((gap
(/ (- time (get-internal-run-time)) internal-time-units-per-second)))
(cond ((< gap 0) (values 0 :late))
((<= gap 0.001) (values 0 :now))
(T (values (- gap 0.001) :soon)))))
(defun sleep-until (time)
(multiple-value-bind (span type)
(how-long-until time)
(when (> span 60) (warn “long wait!”)
(case type
(:late nil)
(:now t)
(:soon
(sleep span)
(unless (sleep-until time) (warn “poor timekeeping”))
t))))
(defmacro with-prompt-and-scheduler ((schedule) (line &optional (input *standard-input*)) &body handle-line-input)
(let ((reader (gensym)) (insched (gensym)))
`(let ((,reader (make-partial-reader ,input) (,insched)))
(flet ((,schedule (in fun &aux (at (+ (get-internal-run-time) (* in internal-time-units-per-second))))
(if (null ,insched) (push (cons at fun) schedule)
(loop for s on ,insched
for ((at2) . y) = s
if (< at at2)
do (psetf (car s) (cons at fun)
(cdr s) (cons (car s) (cdr s)))
(finish-loop)
unless y do (setf (cdr s) (acons at fun nil)) (finish-loop)))))
(loop
(if ,insched
(let ((,insched (pop ,insched)))
(when (sleep-until (car ,insched))
(let ((,line (partial-read ,reader)))
(when ,line ,#handle-line-input)))
(funcall (cdr ,insched)))
(let ((,line (concatenate 'string (get-output-stream-string (second ,reader)) (read-line (first ,reader)))))
,#handle-line))))))))
And then you could use it like:
(let ((count 0))
(with-prompt-and-scheduler (schedule) (line)
(let ((n (read-from-string line)))
(when (realp n)
(schedule n (let ((x (incf count))) (lambda () (format t "Ding ~a ~a~%" x count) (finish-output))))))))
And after running that input 10, then on the next line 5. If you do that quickly you’ll get:
Ding 2 2
Ding 1 2
With the first line appearing after 5 seconds and the second after 10. If you are slow you should get:
Ding 1 1
Ding 2 2
With the first line coming 10 seconds after you enter 10 and the second line coming 5 seconds after you enter 5.
Hopefully this can give you an idea of how to make a program seem to do two things at once.

How do I encode booleans using yason?

I'm trying to use the yason library to encode some data for an API endpoint. Some parts of the return value here are going to be booleans. The problem is
cl-user> (yason:encode nil)
null
NIL
cl-user>
According to their docs, there is a flag named *parse-json-booleans-as-symbols*, but there doesn't seem to be an inverse for encoding. (And the appropriate symbols don't seem to auto-encode appropriately).
cl-user> (yason:encode 'true)
"true" ;; expected `true` rather than `"true"`
"true"
cl-user> (yason:encode 'false)
"false" ;; expected `false` rather than `"false"`
"false"
cl-user>
This isn't a complete answer, but note that nil is the canonical false value in Common Lisp, but also used for lots of other things. The symbol t is the canonical true value, even though any-non nil value will is also a true value. The examples from the YASON documentation show that t is serialized as the JSON true literal:
CL-USER> (yason:encode
(list (alexandria:plist-hash-table
'("foo" 1 "bar" (7 8 9))
:test #'equal)
2 3 4
'(5 6 7)
t nil)
*standard-output*)
[{"foo":1,"bar":[7,8,9]},2,3,4,[5,6,7],true,null]
(#<HASH-TABLE :TEST EQUAL :COUNT 2 {59942D21}> 2 3 4 (5 6 7) T NIL)
If null is treated as a false value by the JSON consumer, the standard behavior with t and nil may be sufficient for your use.
As another option, you could map your false booleans (i.e., nil when used as a boolean) to yason:false in advance. The symbols yason:true and yason:false map to the booleans as you'd expect:
CL-USER> (yason:encode t)
true
;=> T
CL-USER> (yason:encode nil)
null
;=> NIL
CL-USER> (yason:encode 'yason:true)
true
;=> YASON:TRUE
CL-USER> (yason:encode 'yason:false)
false
;=> YASON:FALSE
If you're interested in how I discovered this (I've never used YASON before), I installed YASON with Quicklisp, and then using Slime inspected yason:encode. I discovered that it's a generic function with a number of methods defined:
(DEFGENERIC YASON:ENCODE (YASON::OBJECT &OPTIONAL STREAM))
(DEFMETHOD YASON:ENCODE (EQL ()))
(DEFMETHOD YASON:ENCODE (EQL T))
(DEFMETHOD YASON:ENCODE (EQL NULL))
(DEFMETHOD YASON:ENCODE (EQL YASON:FALSE))
(DEFMETHOD YASON:ENCODE (EQL YASON:TRUE))
(DEFMETHOD YASON:ENCODE LIST)
(DEFMETHOD YASON:ENCODE VECTOR)
(DEFMETHOD YASON:ENCODE HASH-TABLE)
(DEFMETHOD YASON:ENCODE INTEGER)
(DEFMETHOD YASON:ENCODE FLOAT)
(DEFMETHOD YASON:ENCODE RATIO)
(DEFMETHOD YASON:ENCODE STRING)
You can inspect the source of these individually, but the source of the methods on (eql ()) (i.e., (eql NIL)), and of (eql YASON:FALSE) were the important ones.

Isearch return t if found for loop function in Emacs Lisp

How to write a function where whenever a variable is found, it returns t (in order to allow a loop):
(setq x 1)
(while ("backward search for regexp "%x" equals true") ;where x is variable
(setq x (+ x 1))
(insert (concat "%" (int-to-string x)))
)
Example: If %1 (x=1) is found, it will add 1 to x. If %2 (x=2) is found, it will add 1 to x.
Let's say %3 is not found in a backward search, the while loop stops and "%" + "3" is inserted (%3).
I just don't understand the how to return true on a backward-search.
search-backward takes an optional third argument which, when non-nil, tells it to return nil in case the search was unsuccessful:
(setq x 1)
(while (search-backward (format "%%%d" x) nil t)
(setq x (1+ x)))
(insert (format "%%%d" x))
Now, if I try to understand what you really want to do (something like inserting at point the first %d string which doesn't appear before), then you might want to wrap the search inside a save-excursion form to avoid moving the point:
(setq x 1)
(while (save-excursion (search-backward (format "%%%d" x) nil t))
(setq x (1+ x)))
(insert (format "%%%d" x))
With help from Francesco
(defun Navi-insert-question ()
(interactive)
(setq x 1)
(while (save-excursion
(search-backward (concat comment-start " Question: " (int-to-string x)) nil t))
(setq x (+ 1 x)))
(insert (concat comment-start " Question: " (int-to-string x))))
It now results in being able to insert in R, for instance: "# Question: 1", when it exists above in the buffer it will insert "# Question: 2".