Clojure : loading dependencies at the REPL

前端 未结 2 1975
忘了有多久
忘了有多久 2020-12-12 19:11

I recently learned (thanks to technomancy) that, at the REPL ---

This fails:

user=> (:require [clojure.set :as set])
java.lang         


        
2条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2020-12-12 19:41

    I'll go from high-level down to your particular problem:

    How Clojure (or LISPs) Generally Work

    REPLs, or Read-Eval-Print Loops are the core of how LISPs are designed:

    • The reader converts a stream of characters into data structures (called Reader Forms).
    • The evaluator takes collection of reader forms and evaluates them.
    • The printer emits the results of the evaluator.

    So when you enter text into a REPL, it goes through each of these steps to process your input and return the output to your terminal.

    Reader Forms

    First some, clojure reader forms. This will be extremely brief, I encourage you to read or watch (part 1, part 2) about it.

    A symbol in clojure is form that can represent a particular value (like a variable). Symbols themselves can be pass around as data. They are similar to pointers in c, just without the memory management stuff.

    A symbol with a colon in front of it is a keyword. Keywords are like symbols with the exception that a keyword's value are always themselves - similar to strings or numbers. They're identical to Ruby's symbols (which are also prefixed with colons).

    A quote in front of a form tells the evaluator to leave the data structure as-is:

    user=> (list 1 2)
    (1 2)
    user=> '(1 2)
    (1 2)
    user=> (= (list 1 2) '(1 2))
    true
    

    Although quoting can apply to more than just lists, it's primarily used for lists because clojure's evaluator will normally execute lists as a function-like invocation. Using the ' is shorthand to the quote macro:

    user=> (quote (1 2)) ; same as '(1 2)
    (1 2)
    

    Quoting basically specifies data structure to return and not actual code to execute. So you can quote symbols which refers to the symbol.

    user=> 'foo ; not defined earlier
    foo
    

    And quoting is recursive. So all the data inside are quoted too:

    user=> '(foo bar)
    (foo bar)
    

    To get the behavior of (foo bar) without quoting, you can eval it:

    user=> (eval '(foo bar)) ; Remember, foo and bar weren't defined yet.
    CompilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling:(NO_SOURCE_PATH:1)
    user=> (def foo identity)
    #'user/foo
    user=> (def bar 1)
    #'user/bar
    user=> (eval '(foo bar))
    1
    

    There's a lot more to quoting, but that's out of this scope.

    Requiring

    As for require statements, I'm assuming you found the former in the form of:

    (ns my.namespace
        (:require [clojure.set :as set]))
    

    ns is a macro that will transform the :require expression into the latter form you described:

    (require '[clojure.set :as set])
    

    Along with some namespacing work. The basics are described when asking for the docs of ns in the REPL.

    user=> (doc ns)
    -------------------------
    clojure.core/ns
    ([name docstring? attr-map? references*])
    Macro
      Sets *ns* to the namespace named by name (unevaluated), creating it
      if needed.  references can be zero or more of: (:refer-clojure ...)
      (:require ...) (:use ...) (:import ...) (:load ...) (:gen-class)
      with the syntax of refer-clojure/require/use/import/load/gen-class
      respectively, except the arguments are unevaluated and need not be
      quoted. (:gen-class ...), when supplied, defaults to :name
      corresponding to the ns name, :main true, :impl-ns same as ns, and
      :init-impl-ns true. All options of gen-class are
      supported. The :gen-class directive is ignored when not
      compiling. If :gen-class is not supplied, when compiled only an
      nsname__init.class will be generated. If :refer-clojure is not used, a
      default (refer 'clojure) is used.  Use of ns is preferred to
      individual calls to in-ns/require/use/import:
    

    REPL usage

    In general, don't use ns in the REPL, and just use the require and use functions. But in files, use the ns macro to do those stuff.

提交回复
热议问题