zoukankan      html  css  js  c++  java
  • [转]Common Lisp Style Guide

    ref:Common Lisp Style Guide - Ariel Networks Labs

    Package

    One package per one file

    Strangely enough, in case of legacy CL programs, their packages are declared in one file (maybe named "package.lisp"). In other hand, we recommend to declare each packages in each files.

    You should always put like following code at the top of each Lisp files.

    (in-package :cl-user)
    (defpackage style-guide.core
      (:use :cl))
    (in-package :style-guide.core)
    
    ;; body
    

    If you adopt this style on your programs, you will notice you think about dependence of each components. It is a good signal. This style keeps a program to be loosely coupled.

    Avoid :use

    Don't use :use unnecessarily. It is often hard to understand where a function came from. We recommend using :import-from for instead.

    (in-package :cl-user)
    (defpackage style-guide.core
      (:use :cl)
      (:import-from :style-guide.util
                    :funky-feature))
    (in-package :style-guide.core)
    

    We allow you to use :use only if most of symbols are needed or it is obvious.

    (in-package :cl-user)
    (defpackage style-guide.core
      (:use :cl
            :anaphora)
      (:import-from :style-guide.util
                    :funky-feature))
    (in-package :style-guide.core)
    

    Why we recommend such a complicated rule is from our thoughts. We think codes are also documents, and furthermore if we say, they are also novels. If we say from the point, importing symbols are introducing characters. We think it might help readers.

    Annotation

    Use "cl-annot" positively

    Though "Annotation" is not supported in Common Lisp, "cl-annot" provides the feature. We recommend using it almost always. For example, "@export" annotation means exporting the following function or something.

    @export
    (defun plus-ten (x)
      (+ x 10))
    

    If you want to know the truth, that is just a macro actually. So, you can say that just a shorthand. Of course, you can define your own annotations.

    Why we recommend such an ugly syntax? Because it provides transparency to our code.

    For example, defwidget is one of macros in Weblocks. It is just a defclass, actually. Well, really? You have to expand the macro to know that. It is not transparent.

    If I write Weblocks from scratch now, I will provide "@widget" annotation, for instead of defwidget. You can use familiar defclass to define a widget. It makes you be relieved.

    In that way, we can represent the will we won't disturb your code by using annotations. This is another expression we can use.

    Naming

    Surround class name with "<" and ">"

    (defclass <aluminium> (<metal>)
        (color solidity cost))
    

    Surround constants with "+"

    (defconstant +kikuko-inoue-age+ 17)
    

    Surround special vars with "*"

    (defvar *cache-table* (make-hash-table))
    (defparameter *debug* t)
    

    Hierarchical Package Name

    ;; in core.lisp
    (in-package :cl-user)
    (defpackage style-guide.core
      (:use :cl))
    (in-package :style-guide.core)
    
    ;; in util.lisp
    (in-package :cl-user)
    (defpackage style-guide.util
      (:use :cl))
    (in-package :style-guide.util)
    
    ;; in class/metal.lisp
    (in-package :cl-user)
    (defpackage style-guide.class.metal
      (:use :cl))
    (in-package :style-guide.class.metal)
    

    Comment

    Comments are Optional

    All comments are optional. Usually, comments are for writer of the program and it is you in most of the times. If you think that it should be known by users, it must be included in docstring, not comment.

    Comments should end with period

    This is just a rule.

    ;; TODO: rewrite to recursive at tail position.
    (defun factorial (n)
      (if (<= n 1)
        1
        (* n (factorial (1- n))))))
    

    Docstring

    Required (almost always)

    Docstring is always needed for every parts. Don't forget Packages and ASDF Systems.

    You can omit only if it is obvious what to do.

    Class

    Add :type to each slots

    (defclass <aluminium> (<metal>)
        ((color :type string
                :initarg :color
                :initform "white")
         (solidity :type (or integer <solidity>)
                   :initarg :solidity
                   :initform (make-instance '<solidity>))
         (cost :type (or integer null)
               :initarg :cost))
      (:documentation "A class represents Aluminium."))
    

    Don't forget a type "null" for optional slots.

    Macro

    Avoid Macros in really meaning

    You know Macro is one of the strongest feature in Common Lisp. But it is also a dangerous thing. You should avoid using Macro if it is possible.

    This is a really important warning. If you feel it is needed once, you should think this well again. How do other languages manage it? They really manage the problems without macros.

    For example, it is often used that defines something specialized type (like "defwidget"). Must it be a macro, not an annotation? Why don't you use defclass for instead. There are more choices than you think. Macro is the last one to choose.

    Conditional Flow

    Use WHEN, UNLESS if possible

    Don't use if without "else" expression. when is more precise for it.

    Use ETYPECASE, ECASE if possible

    etypecase and ecase are a strict version of typecase and case. If you don't expect other types specified, you should use etypecase or ecase for safety.

    Don't nest conditional flow

    However if is a simple feature and most of languages have it, it could make a program hard to understand.

    ;; Hard to understand
    (defun count-all-numbers (alist)
      (if (null alist)
        0
        (+ (if (listp (first alist))
             (count-all-numbers (first alist))
             (if (numberp (first alist)) 1 0))
           (count-all-numbers (rest alist)))))
    

    Above example should be rewritten as following.

    ;; quoted from "Good Lisp Style"
    (defun count-all-numbers (exp)
      (typecase exp
        (cons   (+ (count-all-numbers (first exp))
                   (count-all-numbers (rest exp))))
        (number 1)
        (t      0)))
    

    A large typecase may be rewritten with defmethod or Polymorphism.

    Keep the condition expression short

    Large condition expressions makes the codes hard to read. If you felt the condition will be larger, you should separate them into another function or method.

    (if (and (person-name user)
             (<= 20 (person-age user)))
      (write-line "this person is valid.")
      (error "Invalid person."))
    
    (defmethod valid-person-p ((person <person>))
      (and (person-name person)
           (<= 20 (person-age person))))
    
    (if (valid-person-p person)
      (write-line "this person is valid.")
      (error "Invalid person."))
  • 相关阅读:
    使用Jenkins进行android项目的自动构建(3)
    使用Jenkins进行android项目的自动构建(2)
    使用Jenkins进行android项目的自动构建(1)
    testlink 从1.8.5 升级到 1.9.8
    779. 第K个语法符号(Leetcode)
    687. 最长同值路径(Leetcode)(递归+树)
    116. 飞行员兄弟(Acwing)(递归+位运算)
    95. 费解的开关(Acwing)(分析+递推)
    Java遇到输入速度瓶颈时的解决办法
    92. 递归实现指数型枚举(Acwing)(递归)
  • 原文地址:https://www.cnblogs.com/Proteas/p/2378892.html
Copyright © 2011-2022 走看看