REPL içinde bir clojure dosyası nasıl yeniden yüklenir


170

REPL'yi yeniden başlatmak zorunda kalmadan Clojure dosyasında tanımlanan işlevleri yeniden yüklemenin tercih edilen yolu nedir. Şu anda, güncellenmiş dosyayı kullanmak için:

  • Düzenle src/foo/bar.clj
  • REPL'i kapat
  • REPL'i aç
  • (load-file "src/foo/bar.clj")
  • (use 'foo.bar)

Ayrıca (use 'foo.bar :reload-all), kaynak hiç değişmediği gibi davranmak yerine, işlevlerin değiştirilmiş gövdelerini değerlendiren ve yeni değerler döndüren gerekli etki ile sonuçlanmaz.

Belgeler:


20
(use 'foo.bar :reload-all)benim için her zaman iyi çalıştı. Ayrıca, (load-file)sınıf yolunuzu doğru ayarladıysanız asla gerekli olmamalıdır. Alacağınız "gerekli etki" nedir?
Dave Ray

Evet, "gerekli etki" nedir? bar.clj"Gerekli etki" hakkında ayrıntılı bir örnek gönderin.
Sridhar Ratnakumar

1
Gerekli etkiyle, bir işleve sahip (defn f [] 1)olsaydım ve tanımını değiştirdiysem (defn f [] 2), bana göre (use 'foo.bar :reload-all)ve fişlevi çağırdıktan sonra 1 değil, 2 döndürmesi gerektiğini düşündüm. fonksiyonun gövdesini değiştirdiğimde REPL'i yeniden başlatmam gerekiyor.
pkaleta

Kurulumunuzda başka bir sorun olmalı ... :reloadya da :reload-allher ikisi de çalışmalıdır.
Jason

Yanıtlar:


196

Veya (use 'your.namespace :reload)


3
:reload-allayrıca çalışması gerekir. OP özellikle bunu söylemiyor, ancak OP'nin geliştirici ortamında yanlış bir şey olduğunu düşünüyorum çünkü tek bir dosya için iki ( :reloadve :reload-all) aynı etkiye sahip olmalı. İşte tam komut :reload-all: (use 'your.namespace :reload-all) Bu, tüm bağımlılıkları da yeniden yükler.
Jason

77

Ayrıca tools.namespace kullanmak gibi bir alternatif var, oldukça verimli:

user=> (use '[clojure.tools.namespace.repl :only (refresh)])

user=> (refresh)

:reloading (namespace.app)

:ok

3
bu cevap daha uygun
Bahadir Cambel

12
Dikkat: koşmak (refresh)da REPL'in gerekli olduğunu unutmasına neden oluyor clojure.tools.namespace.repl. Sonraki çağrılar (refresh)size bir RuntimeException ("Sembol çözülemiyor: bu bağlamda yenileme") verecektir. Yapmak için muhtemelen en iyi şey, ya etmektir (require 'your.namespace :reload-all)sen, belirli bir proje için REPL çok yenilemek istiyorum gidiyoruz biliyorsanız, ya bir hale :devprofili ve eklemek [clojure.tools.namespace.repl :refer (refresh refresh-all)]içindev/user.clj .
Dave Yarwood

1
Tools.namespace'in yazarı tarafından Clojure iş akışı hakkında blog yazısı: thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded
David Tonhofer

61

Kullanarak Clojure kod yeniden yüklenmesi (require … :reload)ve :reload-allbir çok sorunlu :

  • Birbirine bağlı iki ad alanını değiştirirseniz, derleme hatalarını önlemek için bunları doğru sırada yeniden yüklemeyi unutmayın.

  • Tanımları bir kaynak dosyadan kaldırır ve daha sonra yeniden yüklerseniz, bu tanımlar bellekte de kullanılabilir. Diğer kodlar bu tanımlara bağlıysa, çalışmaya devam eder, ancak JVM'yi bir sonraki başlatışınızda bozulur.

  • Yeniden yüklenen ad alanı içeriyorsa defmulti, ilişkili tüm defmethodifadeleri de yeniden yüklemeniz gerekir .

  • Yeniden yüklenen ad alanı içeriyorsa defprotocol, bu protokolü uygulayan tüm kayıtları veya türleri yeniden yüklemeniz ve bu kayıtların / türlerin mevcut örneklerini yeni örneklerle değiştirmeniz gerekir.

  • Yeniden yüklenen ad alanı makro içeriyorsa, bu makroları kullanan tüm ad alanlarını da yeniden yüklemeniz gerekir.

  • Çalışan program, yeniden yüklenen ad alanındaki değerleri kapatacak işlevler içeriyorsa, bu kapalı değerler güncellenmez. (Bu, işlevlerin bir bileşimi olarak "işleyici yığını" nı oluşturan web uygulamalarında yaygındır.)

Clojure.tools.namespace kütüphanesi durumu önemli ölçüde iyileştirir. Ad alanlarının bağımlılık grafiğine dayalı olarak akıllı yeniden yükleme yapan kolay bir yenileme işlevi sağlar.

myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok

Maalesef, refreshişleve başvurduğunuz ad alanı değiştiyse ikinci kez yeniden yükleme başarısız olur . Bunun nedeni, tools.namespace'in yeni kodu yüklemeden önce ad alanının geçerli sürümünü yok etmesidir.

myapp.web=> (refresh)

CompilerException java.lang.RuntimeException: Unable to resolve symbol: refresh in this context, compiling:(/private/var/folders/ks/d6qbfg2s6l1bcg6ws_6bq4600000gn/T/form-init819543191440017519.clj:1:1)

Bu sorun için bir geçici çözüm olarak tam nitelenmiş var adını kullanabilirsiniz, ancak şahsen ben her yenileme üzerinde yazmak zorunda kalmamayı tercih ederim. Yukarıdakilerle ilgili bir başka sorun, ana ad alanını yeniden yükledikten sonra standart REPL yardımcı fonksiyonlarına ( docve gibi source) artık orada referans verilmemesidir.

Bu sorunları çözmek için, güvenilir bir şekilde yeniden yüklenebilmesi için kullanıcı ad alanı için gerçek bir kaynak dosyası oluşturmayı tercih ederim. Kaynak dosyayı koydum ~/.lein/src/user.cljama istediğiniz yere yerleştirebilirsiniz. Dosya, ns bildiriminde aşağıdaki gibi yenileme işlevini gerektirmelidir:

(ns user
  (:require [clojure.tools.namespace.repl :refer [refresh]]))

Şunları yapabilirsiniz kurulum bir Leiningen kullanıcı profili içinde ~/.lein/profiles.cljsınıf yoluna eklenir dosyayı koymak konum bu yüzden. Profil şöyle görünmelidir:

{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
        :repl-options { :init-ns user }
        :source-paths ["/Users/me/.lein/src"]}}

REPL başlatılırken kullanıcı ad alanını giriş noktası olarak ayarladığımı unutmayın. Bu, REPL yardımcı işlevlerine uygulamanızın ana ad alanı yerine kullanıcı ad alanında başvurulmasını sağlar. Bu şekilde yeni oluşturduğumuz kaynak dosyayı değiştirmezseniz kaybolmazlar.

Bu yardımcı olur umarım!


İyi öneriler. Bir soru: neden yukarıdaki ": source-paths" girdisi?
Alan Thompson

2
@DirkGeurs ile :source-pathsI get #<FileNotFoundException java.io.FileNotFoundException: Could not locate user__init.class or user.clj on classpath: >ile birlikte, :resource-pathsherşey yolunda.
fl00r

1
@ fl00r ve hala bu hatayı atar? REPL'yi başlattığınız klasörde geçerli bir project.clj var mı? Bu sorununuzu düzeltebilir.
Dirk Geurs

1
Evet, oldukça standart ve tüm iyi çalışıyor :resource-paths, ben repl içindeki kullanıcı ad alanındayım.
fl00r

1
Bu reloadsorun yüzünden bana yalan söyleyen bir REPL ile çalışmak için harika zaman geçirdim . Sonra çalıştığını düşündüğüm her şeyin artık olmadığı ortaya çıktı. Belki birisi bu durumu düzeltmelidir?
Alper

41

En iyi cevap:

(require 'my.namespace :reload-all)

Bu, yalnızca belirttiğiniz ad alanını yeniden yüklemekle kalmaz, tüm bağımlılık ad alanlarını da yeniden yükler.

Belgeler:

gerektirir


2
lein replColjure 1.7.0 ve nREPL 0.3.5 ile çalışan tek cevap bu . Eğer Clojure yeniyseniz: ad ( 'my.namespace) ile tanımlanır (ns ...)içinde src/... /core.cljörneğin.
Aaron Digulla

1
Bu cevapla ilgili sorun, orijinal sorunun (yükleme dosyası ...) kullanmasıdır, gerek yoktur. Yükleme dosyasından sonra: reload-all öğesini ad alanına nasıl ekleyebilir?
jgomo3

Like ad alanı yapısı proj.stuff.corediskteki dosya yapısını yansıttığı için src/proj/stuff/core.clj, REPL doğru dosyayı bulabilir ve ihtiyacınız yoktur load-file.
Alan Thompson

6

Papachan'ın cevabına dayanan bir astar:

(clojure.tools.namespace.repl/refresh)

5

Bunu Lighttable'da (ve müthiş instarepl'de) kullanıyorum, ancak diğer geliştirme araçlarında da kullanışlı olmalı. Ben aynı zamanda şimdi, geliştirme sırasında ad alanları ilan yerine geliştirme sırasında yeniden yüklendikten sonra asılı fonksiyonları ve multimethods eski tanımları ile aynı sorunu yaşıyordu:

(ns my.namespace)

İsim alanlarımı şöyle beyan ederim:

(clojure.core/let [s 'my.namespace]
                  (clojure.core/remove-ns s)
                  (clojure.core/in-ns s)
                  (clojure.core/require '[clojure.core])
                  (clojure.core/refer 'clojure.core))

Oldukça çirkin ama her isim alanını yeniden değerlendirdiğimde (her ifadenin yeni instarepl sonuçlarını almak için Lighttable'da Cmd-Shift-Enter), tüm eski tanımları uçurur ve bana temiz bir ortam verir. Bunu yapmaya başlamadan önce her birkaç günde bir eski tanımlarla uğraştım ve aklıma geldi. :)


3

Yük dosyasını tekrar denemek ister misiniz?

Bir IDE kullanıyorsanız, genellikle REPL'e bir kod bloğu göndermek için bir klavye kısayolu vardır, böylece ilişkili işlevleri etkili bir şekilde yeniden tanımlar.


1

En kısa sürede (use 'foo.bar)sizin için işler, sizin CLASSPATH'e üzerinde foo / bar.clj veya foo / bar_init.class var demektir. Bar_init.class, bar.clj dosyasının AOT tarafından derlenmiş bir sürümü olacaktır. Bunu yaparsanız (use 'foo.bar), Clojure'un sınıfı clj yerine mi yoksa başka bir yoldan mı tercih ettiğini tam olarak bilmiyorum. Sınıf dosyalarını tercih ederse ve her iki dosyanız da varsa, clj dosyasını düzenlemenin ve ardından ad alanını yeniden yüklemenin bir etkisi olmadığı açıktır.

BTW: CLASSPATH load-filecihazınız usedoğru şekilde ayarlanmadan önce yapmanız gerekmez .

BTW2: load-fileBir nedenden dolayı kullanmanız gerekiyorsa , dosyayı düzenlediyseniz tekrar yapabilirsiniz.


14
Bunun neden doğru cevap olarak işaretlendiğinden emin değilim. Soruyu net olarak cevaplamıyor.
AnnanFay

5
Birisi bu soruya geldiğinde, bu cevabı çok net bulmuyorum.
ctford
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.