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

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 -*-

Related

Using a default value for a function parameter which depends of other parameter

I'd like to create an script which takes an input file and optionally an output file. When you don't pass an output file, the script uses the same filename as the input but with the extension changed. I don't know how to write a default parameter which changes the extension.
#!/usr/bin/env raku
unit sub MAIN(
Str $input where *.IO.f, #= input file
Str $output = $input.IO.extension("txt"), #= output file
Bool :$copy, #= copy file
Bool :$move, #= move file
);
Unfortunately that doesn't work:
No such method 'IO' for invocant of type 'VMNull'
in block <unit> at ./copy.raku line 5
How can I do something like that?
error message is less than awesome but program not working is expected because you have in the signature
Str $output = $input.IO.extension("txt")
but the right hand side returns an IO::Path object with that extension but $output is typed to be a String. That is an error:
>>> my Str $s := "file.csv".IO.extension("txt")
Type check failed in binding; expected Str but got IO::Path (IO::Path.new("file.t...)
in block <unit> at <unknown file> line 1
>>> sub fun(Str $inp, Str $out = $inp.IO.extension("txt")) { }
&fun
>>> fun "file.csv"
Type check failed in binding to parameter '$out'; expected Str but got IO::Path (IO::Path.new("file.t...)
in sub fun at <unknown file> line 1
in block <unit> at <unknown file> line 1
Sometimes compiler detects incompatible default values:
>>> sub yes(Str $s = 3) { }
===SORRY!=== Error while compiling:
Default value '3' will never bind to a parameter of type Str
------> sub yes(Str $s = 3⏏) { }
expecting any of:
constraint
but what you have is far from a literal, so runtime detection.
To fix it, you can either
change to Str() $output = $inp.IO.extension("txt") where Str() means "accept Any object and then cast it to Str". So $output will end up being a string like "file.txt" available in MAIN.
similar alternative: Str $output = $inp.IO.extension("txt").Str but it's repetitive in Str.
change to IO::Path() $output = $inp.IO.extension("txt"). Similarly, this casts to whatever recieved to an IO::Path object, so, e.g., you'll have "file.txt".IO available in $output. If you do this, you might want to do the same for $input as well for consistency. Since IO::Path objects are idempotent to .IO (in eqv sense), no other part of the code needs changing.

How to properly ask an input from user in LispWorks?

I have this code:
(defvar x)
(setq x (read))
(format t "this is your input: ~a" x)
It kinda work in Common Lisp but LispWorks it is showing this error:
End of file while reading stream #<Synonym stream to
*BACKGROUND-INPUT*>.
I mean I tried this: How to read user input in Lisp of making a function. But still shows the same error.
I hope anyone can help me.
You probably wrote these three lines into Editor and then compiled it.
So, you can write this function into Editor:
(defun get-input ()
(format t "This is your input: ~a" (read)))
Compile Editor and call this function from Listener (REPL).
CL-USER 6 > (get-input)
5
This is your input: 5
NIL
You can also use *query-io* stream like this:
(format t "This is your input: ~a" (read *query-io*))
If you call this line in Listener, it behaves like read. If you call it in Editor, it shows small prompt "Enter something:".
As you can see, no global variable was needed. If you need to do something with that given value, use let, which creates local bindings:
(defun input-sum ()
(let ((x (read *query-io*))
(y (read *query-io*)))
(format t "This is x: ~a~%" x)
(format t "This is y: ~a~%" y)
(+ x y)))
Consider also using read-line, which takes input and returns it as string:
CL-USER 19 > (read-line)
some text
"some text"
NIL
CL-USER 20 > (read-line)
5
"5"
NIL

How do I take a reference to new?

Suppose I have the following code:
my constant #suits = <Clubs Hearts Spades Diamonds>;
my constant #values = 2..14;
class Card {
has $.suit;
has $.value;
# order is mnemonic of "$value of $suit", i.e. "3 of Clubs"
multi method new($value, $suit) {
return self.bless(:$suit, :$value);
}
}
It defines some suits and some values and what it means to be a card.
Now, to build a deck, I essentially need to take the cross product of the suits and the values and apply that to the constructor.
The naiive approach to do this, would of course be to just iterate with a loop:
my #deck = gather for #values X #suits -> ($v, $c) {
take Card.new($v, $c);
}
But this is Raku, we have a cross function that can take a function as an optional argument!, so of course I'm gonna do that!
my #deck = cross(#values, #suits, :with(Card.new));
# Unexpected named argument 'with' passed
# in block <unit> at .\example.raku line 36
... wait no.
What about this?
my #deck = cross(#values, #suits):with(Card.new);
# Unexpected named argument 'with' passed
# in block <unit> at .\example.raku line 36
Still nothing. Reference maybe?
my #deck = cross(#values, #suits):with(&Card.new);
# ===SORRY!=== Error while compiling D:\Code\Raku/.\example.raku
# Illegally post-declared type:
# Card used at line 36
I read somewhere I can turn a function into an infix operator with []
my #deck = cross(#values, #suits):with([Card.new]);
# Unexpected named argument 'with' passed
# in block <unit> at .\example.raku line 36
That also doesn't work.
If classes are supposed to just be modules, shouldn't I then be able to pass a function reference?
Also why is it saying 'with' is that's unexpected? If I'm intuiting this right, what it's actually complaining about is the type of the input, rather than the named argument.
The error message is indeed confusing.
The :with parameter expects a Callable. Card.new is not a Callable. If you write it as :with( { Card.new($^number, $^suit) } ), it appears to work.
Note that I did not use $^value, $^suit, because they order differently alphabetically, so would produce the values in the wrong order. See The ^ twigil for more information on that syntax.
The error is LTA, this makes it a little bit better.
To get back to your question: you can find the code object that corresponds to Card.new with ^find_method. However, that will not work, as Card.new actually expects 3 arguments: the invocant (aka self), $value and $suit. Whereas the cross function will only pass the value and the suit.
The title of your question is “How do I take a reference to new?”, but that is not really what you want to do.
Raku being Raku, you can actually get a reference to new.
my $ref = Card.^lookup('new');
You can't use it like you want to though.
$ref(2,'Clubs'); # ERROR
The problem is that methods take a class or instance as the first argument.
$ref(Card, 2,'Clubs');
You could use .assuming to add it in.
$ref .= assuming(Card);
$ref(2,'Clubs');
But that isn't really any better than creating a block lambda
$ref = { Card.new( |#_ ) }
$ref(2,'Clubs');
All of these work:
cross( #values, #suits ) :with({Card.new(|#_)}) # adverb outside
cross( #values, #suits, :with({Card.new(|#_)}) ) # inside at end
cross( :with({Card.new(|#_)}), #values, #suits ) # inside at beginning
#values X[&( {Card.new(|#_)} )] #suits # cross meta-op with fake infix op
do {
sub new-card ($value,$suit) { Card.new(:$value,:$suit) }
#values X[&new-card] #suits
}
do {
sub with ($value,$suit) { Card.new(:$value,:$suit) }
cross(#values,#suits):&with
}

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.

Input stream ends within an object

I want to count the number of rows in a flat file, and so I wrote the code:
(defun ff-rows (dir file)
(with-open-file (str (make-pathname :name file
:directory dir)
:direction :input)
(let ((rownum 0))
(do ((line (read-line str file nil 'eof)
(read-line str file nil 'eof)))
((eql line 'eof) rownum)
(incf rownum )))))
However I get the error:
*** - READ: input stream
#<INPUT BUFFERED FILE-STREAM CHARACTER #P"/home/lambda/Documents/flatfile"
#4>
ends within an object
May I ask what the problem is here? I tried counting the rows; this operation is fine.
Note: Here is contents of the flat file that I used to test the function:
2 3 4 6 2
1 2 3 1 2
2 3 4 1 6
A bit shorter.
(defun ff-rows (dir file)
(with-open-file (stream (make-pathname :name file
:directory dir)
:direction :input)
(loop for line = (read-line stream nil nil)
while line count line)))
Note that you need to get the arguments for READ-LINE right. First is the stream. A file is not part of the parameter list.
Also generally is not a good idea to mix pathname handling into general Lisp functions.
(defun ff-rows (pathname)
(with-open-file (stream pathname :direction :input)
(loop for line = (read-line stream nil nil)
while line count line)))
Do the pathname handling in another function or some other code. Passing pathname components to functions is usually a wrong design. Pass complete pathnames.
Using a LispWorks file selector:
CL-USER 2 > (ff-rows (capi:prompt-for-file "some file"))
27955
Even better is when all the basic I/O functions work on streams, and not pathnames. Thus you you could count lines in a network stream, a serial line or some other stream.
The problem, as far as I can tell, is the "file" in your (read-line ... ) call.
Based on the hyperspec, the signature of read-line is:
read-line &optional input-stream eof-error-p eof-value recursive-p
=> line, missing-newline-p
...which means that "file" is interpreted as eof-error-p, nil as eof-value and 'eof as recursive-p. Needless to say, problems ensue. If you remove "file" from the read-line call (e.g. (read-line str nil :eof)), the code runs fine without further modifications on my machine (AllegroCL & LispWorks.)
(defun ff-rows (dir file)
(with-open-file
(str (make-pathname :name file :directory dir)
:direction :input)
(let ((result 0))
(handler-case
(loop (progn (incf result) (read-line str)))
(end-of-file () (1- result))
(error () result)))))
Now, of course if you were more pedantic then I am, you could've specified what kind of error you want to handle exactly, but for the simple example this will do.
EDIT: I think #Moritz answered the question better, still this may be an example of how to use the error thrown by read-line to your advantage instead of trying to avoid it.