clojure read-eval-print-loop

Clojure: cargar dependencias en el REPL



read-eval-print-loop (2)

Iré de alto nivel a tu problema particular:

Cómo funcionan Clojure (o LISP) en general

Los REPLs, o Read-Eval-Print Loops son el núcleo de cómo se diseñan los LISP:

  • El lector convierte una secuencia de caracteres en estructuras de datos (llamadas Formularios de Reader).
  • El evaluador toma una colección de formularios de lectores y los evalúa.
  • La impresora emite los resultados del evaluador.

Entonces, cuando ingresa texto en un REPL, pasa por cada uno de estos pasos para procesar su entrada y devolver la salida a su terminal.

Formularios de lector

Primero algunas formas de lector de clojure. Esto será extremadamente breve, lo animo a read o mirar ( parte 1 , parte 2 ) al respecto.

Un símbolo en clojure es una forma que puede representar un valor particular (como una variable). Los símbolos mismos se pueden pasar como datos. Son similares a los punteros en c, simplemente sin las cosas de administración de memoria.

Un símbolo con dos puntos al frente es una palabra clave . Las palabras clave son como símbolos, con la excepción de que el valor de una palabra clave siempre son ellos mismos, similar a cadenas o números. Son idénticos a los símbolos de Ruby (que también están prefijados con dos puntos).

Una cita delante de un formulario le dice al evaluador que abandone la estructura de datos tal como está:

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

Aunque las citas pueden aplicarse a más que solo listas, se usa principalmente para listas porque el evaluador de clojure normalmente ejecutará listas como una invocación similar a una función. Usar el '' es una abreviatura de la macro de cita:

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

Citar básicamente especifica la estructura de datos para devolver y no el código real para ejecutar. Así que puedes citar símbolos que hagan referencia al símbolo.

user=> ''foo ; not defined earlier foo

Y citar es recursivo. Entonces, todos los datos que contiene se citan también:

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

Para obtener el comportamiento de (foo bar) sin citar, puede evaluarlo:

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

Hay mucho más para citar, pero eso está fuera de este alcance.

Requiriendo

En cuanto a las declaraciones requeridas, asumo que encontraste las primeras en la forma de:

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

ns es una macro que transformará la expresión: require en la última forma que describiste:

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

Junto con algunos trabajos de creación de espacios de nombres. Los conceptos básicos se describen al solicitar los documentos de ns en 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:

Uso de REPL

En general, no use ns en REPL, y simplemente use las funciones de require y use . Pero en los archivos, use la macro ns para hacer esas cosas.

Recientemente aprendí (gracias a technomancy) que, en el REPL ---

Esto falla:

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

Mientras que esto tiene éxito:

user=> (require ''[clojure.set :as cs]) nil

al cargar la clase clojure.set

Contexto: la primera línea se copió de un archivo fuente de espacio de nombres.

Mi pregunta principal es: ¿Cuál es el cambio que hemos hecho, intercambiando los caracteres: y '', que ahora permite el éxito del último comando?

Mi segunda pregunta es, en general, ¿cuáles son las pautas para hacer cosas en el REPL, en comparación con hacer cosas en los archivos fuente normales de Clojure? Supongamos aquí que podemos cargar nuestra repl desde la raíz de un proyecto LEININGEN, por lo que al menos los archivos jar estarán disponibles en el disco en el subdirectorio de dependencias.


La diferencia es que require es una función utilizada para importar código, mientras que :require es una palabra clave.

Recuerde lo que sucede cuando utiliza una palabra clave como función:

=> (type :require) clojure.lang.Keyword => (:require {:abc 1 :require 14}) 14

se ve a sí mismo en el mapa. Entonces, cuando pasa [clojure.set :as set] a una palabra clave, está tratando de evaluarlo a un vector y falla porque no sabe qué es clojure.set . Los documentos de Clojure dicen:

Las palabras clave implementan IFn para invoke () de un argumento (un mapa) con un segundo argumento opcional (un valor predeterminado). Por ejemplo (: mykey my-hash-map: none) significa lo mismo que (get my-hash-map: mykey: none).

Es posible que haya sido confundido por la macro ns :

(ns foo.bar (:refer-clojure :exclude [ancestors printf]) (:require (clojure.contrib sql sql.tests)) ;; here''s :require! (:use (my.lib this that)) (:import (java.util Date Timer Random) (java.sql Connection Statement)))