Request input regarding code style / best practices - coding-style

I'm trying to implement a programming language in Common Lisp by following this implementation in Java (https://craftinginterpreters.com/control-flow.html).
One of the things that was being really bothersome is plenty of slot-values everywhere.
Granted I can add a line in each function with with-slots, but that's also repetitive, and given that my symbols are not :used but I refer to them by the package, as there is plenty of symbols from many modules and I don't want to lose track from where they come from, even with-slots requires the full qualified name. All this seems to me like very bad code smell.
I googled and found rutils. With its #object.slot accessor I managed to clean up my code immensely. Just look at the last commit https://github.com/AlbertoEAF/cl-lox/commit/84c0d62bf29bff9a2e0e77042a9108c168542b96?diff=split excerpt:
(I could paste the code but all of it is available on that repo, I wanted to show the diff highlights)
Not only it removes some characters, but more importantly, there's one less level of depth (slot-value call) and parenthesis to think about - which helps a lot but for the trivial functions.
It gets worse when I have lots and lots of symbol names and can no longer export them all because I start having conflicts in symbols.
Can you guys give me input on the code? This looks much better now but I'm curious as if to are better ways to go about it?
Thanks!

Defining an accessor function in the class definition would also save you some typing while not having to use non-standard syntax
If you define a CLOS class like this:
(defclass person ()
((name
:initarg :name)))
(defparameter *p* (make-instance 'person
:name "John"))
The only way to access then the slot value of name slot in *p* are:
(slot-value *p* 'name)
;; "John"
(with-slots (name) *p*
name)
;; "John"
(with-slots ((nm name)) *p*
nm)
;; "John"
But if you define for each slot an :accessor, you can use the function name
given as argument of :accessor and don't have to use slot-value or with-slots, to
access and to mutate it (setf-able!).
(defclass person ()
((name
:initarg :name
:accessor nm)))
(nm *p*)
;; "John"
(setf (nm *p*) "Doe")
;; "Doe"
(nm *p*)
;; "Doe"
The convention however is, to use the slot name also as the :accessor method's
name:
(defclass person ()
((name
:initarg :name
:accessor name))) ;; but better use the slot name as accessor
(name *p*)
;; "John"
(setf (name *p*) "Doe")
(name *p*)
;; "Doe"
The :accessor method (generic function) is specific for the objects of
this class. Therefore you don't need to have fear of a name-space clash.
(defclass house ()
((address
:initarg :address
:accessor addr)))
;; you cannot use `address` because it is already occupied
;; by a system's function/symbol -> see under `(describe 'address)`
(defparameter *h* (make-instance 'house :address "Bakerstreet 1"))
(name *h*)
;; EVAL: undefined function name OR:
;; NO-APPLICABLE-METHOD error (in the case
;; that other classes exist with a `name` accessor method.
(addr *h*)
;; "Bakerstreet 1"
(addr *p*)
;; *** - NO-APPLICABLE-METHOD: When calling #<STANDARD-GENERIC-FUNCTION ADDR>
;; with arguments (#<PERSON #x1B06B79E>), no method is applicable.
;;

Related

Call to the next most specific method does not work

Consider the class account :
(defclass account ()
((name :initarg :name :reader name)
(balance :initarg :balance :initform 0.00 :accessor balance)
(interest-rate :allocation :class :initform 0.06
:reader interest-rate)))
For this class, we define a method withdraw :
(defmethod withdraw ((acct account) amt)
(if (< amt (balance acct))
(decf (balance acct) amt)
'insufficient-funds))
And, another class password-account , that is a subclass of account:
(defclass password-account (account)
((password :initarg :password :reader password )))
And, the method withdraw, for this class:
(defmethod withdraw ((acct password-account) amt pass)
(if (equal (password acct) pass)
(call-next-method acct amt )
'wrong-password))
But this gives an error :
The generic function
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::WITHDRAW (1)>
takes 2 required arguments; was asked to find a method with
specializers
(#<STANDARD-CLASS COMMON-LISP-USER::PASSWORD-ACCOUNT>
#1=#<SB-PCL:SYSTEM-CLASS COMMON-LISP:T> #1#)
[Condition of type SB-PCL::FIND-METHOD-LENGTH-MISMATCH]
See also:
Common Lisp Hyperspec, FIND-METHOD [:function]
Restarts:
0: [RETRY] Retry SLIME REPL evaluation request.
1: [*ABORT] Return to SLIME's top level.
2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1005308033}>)
Why is this happening? And what does
was asked to find a method with specializers
mean here?
Here, the primary withdraw function had two arguments acct and amt, so in order to call it from a more specific method, which uses 3 arguments instead of 2, we can provide call-next-method with the arguments of the less specific withdraw method. But this still isn't working.
Any help appreciated.
Congruent lambda lists for generic functions
Methods of a generic function need to have congruent lambda lists. The language standard describes what that means: Congruent Lambda-lists for all Methods of a Generic Function.
As you can see the first rule says:
Each lambda list must have the same number of required parameters.
Required parameters tell us which arguments always have to be provided. Generic functions additionally allow optional, keyword and rest arguments. But there is no dispatch over these. The dispatch only works over the required arguments and all of those.
Having the same number of required parameters makes dispatch easier and allows the compiler to check for function calls with the wrong number of arguments.
Optional parameters need to be congruent, too
Note also that all methods of a generic function need to have the same number of optional parameters. See the second rule in the standard.
Wording
a parameter is something like a named variable in a lambda list
an argument is provided in a call to a function
Examples:
(defun foo (a b) (list a b))
a and b are parameters for the function foo.
(foo (+ 2 3) (* 4 5))
5 and 20 are the two arguments for the call of the function foo.

Racket: What does it mean to bind names to the index?

I'm writing code where the user picks from a list of choices. I have defined my list in the following:
;contains the options the user can pick
(define choices (list "choice1" "choice2" "choice3" "choice4" "choice5" "choice6" "OPENING VALUE" "HIGHEST VALUE" "LOWEST VALUE" "CLOSING VALUE" "VOLUME OF SHARES" "ADJUSTED CLOSING VALUE"))
My button gets the name from the list from the following code(only showing one example). In this case it takes the third item from the list:
[label (list-ref choices 2)]
and when I want to change the name then I use the line of code:
(send choice-4-9 set-label (list-ref choices 9))
My prof commented that I should bind names to 6 7 etc so your code would be readable. I'm still a little confused on what he meant by that and how I would do so.
He means for each index, define an identifier bound to that index, ideally named after what it means, e.g.:
(define choice1 0)
(define choice2 1)
(define choice3 2)
....
So now you can write [label (list-ref choices choice3)] instead of [label (list-ref choices 2)]. The code is more readable, and easier to change if necessary because you can change the binding of the identifier rather than every place where the number appears in code.
Incidentally, what you're doing now is called using "magic numbers."
The previous answer is good and I have upvoted it. I only want to mention that a common data structure for something like this is an association list, where you associate something like a symbol or number to a value, and then look it up using assq, assv, or assoc depending on the whether your lookup name requires eq?, eqv?, or equal? respectively. Consider:
(define choices
'((shoot . "Shoot!")
(run . "Run away!")
(reload . "Reload")
(appraise . "Search your surroundings")))
(define (get-label choice)
(let ((result (assq choice choices)))
(if (not result)
(error "???") ; handle as you see fit
(cdr result))))
;;;;;;;;;;;;;;;;
Welcome to DrRacket, version 6.4 [3m].
Language: racket/base [custom]; memory limit: 8192 MB.
> (get-label 'shoot)
"Shoot!"
>

cons to empty list of type not working

I'm working through the Programming Languages: Application and Interpretation book chapter 6 http://cs.brown.edu/courses/cs173/2012/book/From_Substitution_to_Environments.html
I've applied a fix as described in the book but the cons is not adding the type to the empty list refered to in the source.
I think it's a pass-by-value/pass-by-ref thing, any clues on how to set the mt-env when it's not being passed in as a parameter?
#lang plai-typed
;; Binding types
(define-type Binding
[bind (name : symbol) (val : number)])
;; some helper functions:
(define-type-alias Env (listof Binding))
(define mt-env empty)
(define extend-env cons)
;; testing function
(define (addBinding [b : Binding] [env : Env])
(extend-env b env)
)
(addBinding (bind 'x 5) mt-env) ;; returns (list (bind x 5))
(display mt-env) ;; returns empty list
Below is a link to the complete code for context if required, the interp function's appC case is the specific location of my problem area, thanks.
https://github.com/MickDuprez/plai/blob/master/Chapter%206/chapter-6.rkt
After re-reading the last part of this chapter a few times I don't think there is a simple solution to this problem. The 'change' only makes the revised interpreter behave the same as the previous 'substitution' interpreter but highlights the problem of scope with a special test case.
This is eluded to in the next part '6.4 Scope' where the author writes:
"The broken environment interpreter above implements what is known as dynamic scope."
I'm sure this will be addressed in future chapters, thanks for looking anyway.

Access Java fields dynamically in Clojure?

I'm a novice in clojure and java.
In order to access a Java field in Clojure you can do:
Classname/staticField
which is just the same as
(. Classname staticField)
(correct me if I'm wrong)
How can I access a static field when the name of the field in held within a variable? i.e.:
(let [key-stroke 'VK_L
key-event KeyEvent/key-stroke])
I want key-stroke to be evaluated into the symbol VK_L before it tries to access the field.
In this case you might want to do something like the following:
user=> (def m 'parseInt)
#'user/m
user=> `(. Integer ~m "10")
(. java.lang.Integer parseInt "10")
user=> (eval `(. Integer ~m "10"))
10
If you feel like it's a bit hack-ish, it's because it really is such. There might be better ways to structure the code, but at least this should work.
I made a small clojure library that translates public fields to clojure maps using the reflection API:
(ns your.namespace
(:use nl.zeekat.java.fields))
(deftype TestType
[field1 field2])
; fully dynamic:
(fields (TestType. 1 2))
=> {:field1 1 :field2 2}
; optimized for specific class:
(def-fields rec-map TestType)
(rec-map (TestType. 1 2))
=> {:field1 1 :field2 2}
See https://github.com/joodie/clj-java-fields
Reflection is probably the proper route to take, but the easiest way is
(eval (read-string (str "KeyEvent/" key-stroke)))
This will just convert the generated string into valid clojure and then evaluate it.
You can construct the right call as an s-expression and evaluate it as follows:
(let [key-stroke 'VK_L
key-event (eval `(. KeyEvent ~key-stroke))]
....do things with your key-event......)
However, if what you are trying to do is test whether a particular key has been pressed, you may not need any of this: I usually find that it is easiest to write code something like:
(cond
(= KeyEvent/VK_L key-value)
"Handle L"
(= KeyEvent/VK_M key-value)
"Handle M"
:else
"Handle some other key")

Getting stock prices from Yahoo with Elisp?

I would like to use Yahoo to get stock prices from within an Emacs Lisp program. I have two questions.
How do I make the http GET?
What is the best what to store the data in Elisp so I can make comparisons of the data? In other words, should I use one hash table, several hash tables, or lists to represent that data returned from Yahoo?
Here's the basic outline of what I'd like to do.
;; Call Yahoo to get equity prices
;;
;; Yahoo Input:
;; http://download.finance.yahoo.com/d/quotes.csv?s=AAPL+GOOG&f=sb2b3jkm6
;; Yahoo Output:
;; "AAPL",211.98,211.82,78.20,215.59,+17.90%
;; "GOOG",602.94,601.69,282.75,629.51,+18.27%
;;
;; Symbol, ask, bid, 52 week low, 52 week high, % change from 200 day mavg
;;
;; Yahoo format described here: http://www.gummy-stuff.org/Yahoo-data.htm
(defun get-price-url (tickers)
"
s = symbol
b2 = ask real-time
b3 = bid real-time
j = 52 week low
k = 52 week high
"
(concat "http://download.finance.yahoo.com/d/quotes.csv?s="
(mapconcat 'identity tickers "+") "&f=sb2b3jk"))
(setq lst '("AAPL" "GOOG" "MSFT" "ORCL"))
(setq url (get-price-url lst))
;; Call Yahoo with Url, process results and place in a data structure
;;
;; Return results sorted by largest change in 200 day mavg, in descending order
;;
Here's some code to get you started; I show how to grab the url to a buffer, parse each line and then display the ticker and price of each item. You can modify it from there to do what you need.
This parses each line of stock data into a list, and it's straight forward to grab the values using the first, second, third functions, or using nth. You can write functions to grab each element you want, such as get-ticker(quote) which would just return (first ticker)
I wouldn't over think what kind of data structure to use; whatever is easiest is fine. If you need high performance then you shouldn't be using emacs lisp for this anyway.
(defun test()
(interactive)
(let ((quotes (get-quotes '("AAPL" "GOOG" "MSFT" "ORCL" "ERTS" "THQI") "sb")))
(show-quotes quotes)))
(defun show-quotes(quotes)
(dolist (quote quotes)
(message (format "%s $%.2f" (first quote) (string-to-number (second quote))))))
(defun get-quotes(tickers field-string)
"Given a list of ticker names and a string of fields to return as above, this grabs them
from Yahoo, and parses them"
(let ((results-buffer (get-yahoo-quotes-to-buffer (get-price-url tickers field-string))))
(switch-to-buffer results-buffer)
(parse-quote-buffer results-buffer)))
(defun get-price-url (tickers field-string)
"Set up the get url"
(concat "http://download.finance.yahoo.com/d/quotes.csv?s="
(mapconcat 'identity tickers "+")
"&f=" field-string))
(defun get-yahoo-quotes-to-buffer(url)
"Retrieve the quotes to a buffer and return it"
(url-retrieve-synchronously url))
(defun parse-quote-buffer(b)
"Parse the buffer for quotes"
(goto-line 1)
(re-search-forward "^\n")
(beginning-of-line)
(let ((res nil))
(while (> (point-max) (point))
(setf res (cons (split-string (thing-at-point 'line) ",") res))
(forward-line 1))
(reverse res)))
Check out http://edward.oconnor.cx/elisp/. Edward has some examples of interacting with various services using HTTP, and if you can't find a Yahoo client library you could write one using these techniques.

Resources