Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will the bindings defined in `letfn` be renewed every time `sqrt` is called?

Tags:

clojure

Provided that abs and avg are defined:

(defn sqrt [x]
  (letfn [(s [guess]
            (if (good-enough? guess)
              guess
              (s (improve guess))))
          (good-enough? [guess]
            (< (abs (- (square guess) x)) 0.0001))
          (improve [guess]
            (avg (/ x guess) guess))]
    (s 1.0)))

Please ignore for a sec that I'm reinventing the wheel here. :) This only serves as an example.

If so, is there a way to get around this so the names won't be bound again and again every time the function is called, and without introducing more names outside the function?

like image 940
damonh Avatar asked Jan 04 '17 08:01

damonh


2 Answers

Will the functions defined in letfn be redefined every time sqrt is called?

Yes.

The bindings of s and good-enough will be remade every time the sqrt function is entered. The things they are bound to are constructed afresh.

  • The s and good-enough functions (effectively fn special forms) are compiled once, when the (defn sqrt ... ) is performed.
  • They are closed over x for every call of sqrt.
  • The closures might be implemented on the JVM as inner classes complying with the IFn interface.
  • The closures/function-objects are constructed every time sqrt is entered, though the classes that they exemplify have been long compiled.

It depends what you mean by redefined. I thought not, but the elements on both sides of the bindings are renewed. So now I think so.


is there a more idiomatic way to write the square root function?

Yes.

On the JVM ...

(defn sqrt [x] (Math/sqrt x))

(sqrt 2);1.4142135623730951
like image 113
Thumbnail Avatar answered Nov 15 '22 09:11

Thumbnail


The short answer is "yes", the bindings will be renewed. However, this is as cheap as instantiating a very simple class. You can see below the decompiled java classes how the clojure is implemented with a simple parameter and how nested functions will result in just some more classes. So this is still very cheap.

public final class user$sqrt extends AFunction {
    public static final Object const__0 = Double.valueOf(1.0D);

    public static Object invokeStatic(Object x) {
        user$sqrt$s__44945 s = null;
        user$sqrt$good_enough_QMARK___44947 good_enough_QMARK_ = null;
        user$sqrt$improve__44949 improve = null;
        s = new user$sqrt$s__44945(good_enough_QMARK_, improve);
        good_enough_QMARK_ = new user$sqrt$good_enough_QMARK___44947(x);
        Object var10002 = x;
        x = null;
        improve = new user$sqrt$improve__44949(var10002);
        user$sqrt$s__44945 var10000 = (user$sqrt$s__44945)s;
        ((user$sqrt$s__44945)s).good_enough_QMARK_ = good_enough_QMARK_;
        var10000.improve = improve;
        user$sqrt$good_enough_QMARK___44947 var4 = (user$sqrt$good_enough_QMARK___44947)good_enough_QMARK_;
        user$sqrt$improve__44949 var5 = (user$sqrt$improve__44949)improve;
        return ((IFn)s).invoke(const__0);
    }

}

public final class user$sqrt$good_enough_QMARK___44947 extends AFunction {
    Object x;
    public static final Var const__1 = (Var)RT.var("user", "abs");
    public static final Var const__3 = (Var)RT.var("user", "square");

    public user$sqrt$good_enough_QMARK___44947(Object var1) {
        this.x = var1;
    }

    public Object invoke(Object guess) {
        DO var10000 = (DO)const__1.getRawRoot();
        DO var10001 = (DO)const__3.getRawRoot();
        Object var10002 = guess;
        guess = null;
        return Numbers.lt(var10000.invokePrim(RT.doubleCast((Number)Numbers.minus(var10001.invokePrim(RT.doubleCast((Number)var10002)), this.x))), 1.0E-4D)?Boolean.TRUE:Boolean.FALSE;
    }
}

public final class user$sqrt$improve__44949 extends AFunction {
    Object x;
    public static final Var const__0 = (Var)RT.var("user", "avg");

    public user$sqrt$improve__44949(Object var1) {
        this.x = var1;
    }

    public Object invoke(Object guess) {
        DDO var10000 = (DDO)const__0.getRawRoot();
        double var10001 = RT.doubleCast((Number)Numbers.divide(this.x, guess));
        Object var10002 = guess;
        guess = null;
        return var10000.invokePrim(var10001, RT.doubleCast((Number)var10002));
    }
}
like image 2
ClojureMostly Avatar answered Nov 15 '22 09:11

ClojureMostly