Why should I use (flush) in this code?

This is my first contact with Clojure, so I tried to write a simple script that provides translation based on wikipedia (any criticism / comments are appreciated)

The problem is this: when I remove (flush) from translate, the script outputs nil instead of the translated word. Why? I am clearly missing something, but what? (println translations) gives the same result as flush (at the beginning I tried with a dose of / doall but no results)

(using Clojure 1.2 and testing in eclipse 3.7.2 counterclockwise)

Code:

(ns wiki-translate
    (:require [clojure.contrib.http.agent :as h])
)

(defn get-url
    ([lg term] (str "http://" lg ".wikipedia.org/wiki/" term))
)

(defn fetch-url
    ([url] (h/string (h/http-agent url)))
)

(defn get-translations
    ([cnt]  (apply sorted-map (flatten (for  [s (re-seq #"(?i)interwiki-([^\"]+).*wiki\/([^\"]+)\".*/a>" cnt)] [(s 1) (s 2)])))))

(defn translate
    [term src-lg tgt-lg] (
        (def translations (get-translations (fetch-url (get-url src-lg term)))  )
        (flush)
        (if (contains?  translations tgt-lg) (get translations tgt-lg) "<NOT FOUND>")               
    )           
)

(println (translate "Shark" "en" "fr"))

      

+3


source to share


2 answers


The function translate

has an extra level of parentheses and (flush)

makes it work by accident. No (flush)

code

((def translations (get-translations (fetch-url (get-url src-lg term))))
 (if (contains?  translations tgt-lg) (get translations tgt-lg) "<NOT FOUND>"))

      

Clojure evaluates this form according to its evaluation rules , evaluating two subforms and calling the first as a function. When evaluating subforms, the form becomes

(#'translations
 "Requin")

      

because the first form returns the defined Var and determines it in time for the second form to succeed in the search. When you call Var as a function, the call is delegated to the value of Var , which is a map, and since map implements a function call as a view , the effect is to find "Requin" in the map. The map has no element with this key, so the value is nil.



When added (flush)

in the meantime, the same process occurs:

((def translations (get-translations (fetch-url (get-url src-lg term))))
 (flush)
 (if (contains?  translations tgt-lg) (get translations tgt-lg) "<NOT FOUND>"))

      

first evaluated in

(#'translations
 nil
 "Requin")

      

and the display is called again, which is the value #'translations

. This time, the effect will appear nil

, with "Requin" as the default, returned if nil

not found on the map.

+4


source


You need to use let

instead def

inside the translation function:

(defn translate
    [term src-lg tgt-lg] 
    (let [translations (get-translations (fetch-url (get-url src-lg term)))]
      (if (contains?  translations tgt-lg) (get translations tgt-lg) "<NOT FOUND>")))   

      

let is used to create local binding for forms in a let block. Using def creates a global binding (in the current namespace). So basically after your output code is executed, you can use var transalations

outside of the function because it is created using def in the global scope.



I'm not really sure what flash is supposed to do with def in order to make it work. Probably someone with a deep knowledge of how def works can shed some light on this, and it would be interesting to know for sure.

UPDATE:

The interesting wrapping of the function body do

makes it work with def

no flush, but that's not what you should be doing. Usage let

is the preferred way. do

is used to execute a series of expressions that have side effects, so it appears to def

be a kind of expression of side effects and using do

or flush

"actually" doing the operation of side effects. do

/ p>

+3


source







All Articles