2011-07-30 10 views
9

Załóżmy, że chcę zbudować dużą bibliotekę klauzurową z kilkoma komponentami. Jako programista chciałbym zachować wiele komponentów w osobnych przestrzeniach nazw, ponieważ wiele funkcji pomocniczych może mieć podobne nazwy. Niekoniecznie chcę robić rzeczy prywatne, ponieważ w ekstremalnych przypadkach mogą one być użyteczne, a utrudnienia prywatne nie są dobre. (Innymi słowy, chciałbym zasugerować użycie kodu, nie całkowicie uniemożliwić użycie).W jaki sposób organizujesz nazwy funkcji podczas budowania bibliotek clojure do użytku publicznego?

Chciałbym jednak, aby użytkownicy biblioteki działali w przestrzeni nazw z połączeniem podzbioru wielu funkcji w każda biblioteka podrzędna. Jaki jest idiomatyczny lub najlepszy sposób na zrobienie tego? Jednym z rozwiązań, które przychodzi mi do głowy, jest napisanie makra, które generuje: wymaga i tworzy nowe mapowanie var, definiując podaną listę nazw zmiennych (zobacz pierwszy przykład kodu). Czy istnieją kompromisy z tą metodą, takie jak to, co dzieje się z rozszerzaniem typów? Czy istnieje lepszy sposób (lub wbudowany)?

Makro przykład (src/myLib/public.clj):

(ns mylib.public 
    (:require [mylib.a :as a]) 
    (:require [mylib.b :as b])) 

(transfer-to-ns [+ a/+ 
        - b/- 
        cat b/cat 
        mapper a/mapper]) 

Ponownie, w celu wyjaśnienia, ostatecznym celem byłoby mieć jakiś plik w innych projektach tworzonych przez użytkowników myLib aby być w stanie dokonać coś jak (src/someproject/core.clj):

(ns someproject.core 
    (:require [mylib.public :as mylib])) 

(mylib/mapper 'foo 'bar) 

@Jeremy Wall, pamiętać, że proponowane rozwiązanie nie napełnić moje potrzeby. Załóżmy, że istnieje następujący kod.

myLib/a.clj:

(ns mylib.a) 

(defn fa [] :a) 

myLib/b.clj:

(ns mylib.b) 

(defn fb [] :b) 

myLib/public.clj:

(ns mylib.public 
    (:use [mylib.a :only [fa]]) 
    (:use [mylib.b :only [fb]])) 

somerandomproject/core.clj (Zakładamy, że ścieżki klasy są ustawione prawidłowo)

(ns somerandomproject.core 
    (:require [mylib.public :as p]) 

;; somerandomproject.core=> (p/fa) 
;; CompilerException java.lang.RuntimeException: No such var: p/fa, compiling:  (NO_SOURCE_PATH:3) 
;; somerandomproject.core=> (mylib.a/fa) 
;; :a 

Jeśli zauważysz, że funkcje "using" w pliku mylib/public.clj NIE zezwalają public.clj na DOSTARCZENIE tych zmiennych do pliku użytkownika somerandomproject/core.clj.

Odpowiedz

9

może okazać się interesujący patrzeć jak biblioteka jak Compojure lub Lamina organizuje „publiczne” API.

Lamina ma "publiczne" przestrzenie nazw, takie jak lamina.api, które służą do aliasu (przy użyciu Zach-owego import-fn z jego ostatniego biblioteki) funkcji z "wewnętrznych" przestrzeni nazw, takich jak lamina.core.pipeline. Przy odrobinie dokumentów służy to wyraźnemu wytyczeniu nsów, z którymi mogą się zmierzyć publiczność, od elementów, które mogą ulec zmianie.Odkryłem, że główną wadą tej strategii jest to, że import-fn znacznie utrudnia przechodzenie (w emacs) z użycia funkcji do jej implementacji. Lub, aby dowiedzieć się, która funkcja ma na przykład korzystać z clojure.repl/source.

Biblioteka taka jak Compojure używa prywatnych zmiennych do oddzielania publicznych/prywatnych części funkcji (patrz na przykład compojure.core). Główną wadą funkcji "prywatnych" jest to, że możesz później odkryć, że chcesz je narazić lub że to sprawia, że ​​testowanie jest bardziej skomplikowane. Jeśli kontrolujesz bazę kodu, nie uważam, aby aspekty prywatne były wielką sprawą. Problem z testowaniem można łatwo obejść, używająC# 'foo.core/my-function, aby odnieść się do funkcji prywatnej.

Generalnie używam czegoś bardziej przypominającego styl Compojure niż Lamina, ale brzmi, jakbyś wolał coś bardziej podobnego do Laminy.

+0

Dobra odpowiedź. To jest więcej, czym byłem szukałem. Próbowałem już stylu compojure.core, ale zostałem dotknięty ograniczeniami, o których mówisz, więc szukałem potencjalnych alternatyw. Zbadam blaszkę i sprawdzę, jak to działa, zanim zaakceptujesz. – bmillare

+0

Cascalog jest zorganizowany wzdłuż podobnych linie do Laminy, ale trochę mniej ściśle - wiele z głównych rzeczy, których potrzebujesz, jest albo w aliasach w cascalog.api ns, ale możesz także sięgnąć do podstawowych ns dla bardziej zaawansowanych funkcji. –

2

Nie jestem do końca pewien, o co pytasz. Myślę, że może chcesz wiedzieć, jaka jest najlepsza praktyka do importowania publiczności z przestrzeni nazw dla wspólnych funkcji narzędziowych? W tym przypadku funkcja odnosi się to, co szukasz myślę: http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/refer

(refer mylib.a :only [+]) 
(refer mylib.b :only [-]) 

To importuje elementy publiczne w przestrzeni nazw do bieżącej przestrzeni nazw. Jednak preferowaną metodą byłoby to zrobić w deklaracji przestrzeni nazw z: użyj dyrektywa

(ns (:use (mylib.a :only [+]) 
      (mylib.b :only [-]))) 
+0

Chodzi o to, aby stworzyć przestrzeń nazw, której użytkownicy będą wymagać. Nie służy to do rozwijania TEGO pliku clj, ale do rozwijania innych plików clj, które będą wymagały lub użycia utworzonego pliku. Tak więc w moim przykładzie, w formie ns, masz coś takiego (: require [mylib.public: as mylib] – bmillare

+0

twoje proponowane rozwiązanie nie spełnia moich potrzeb, zobacz wyjaśnienie – bmillare

Powiązane problemy