Search code examples
clojurenullabletype-hinting

Nullable Double Type-Hint


I'm writing a neural net, and have the following definition of a Node:

(defrecord Node [^double input-sum ^double last-output])

input-sum is the running sum of it's input; pre-activation. last-output is it's activated value.

My original thought was to create an initial node like (->Node 0 nil). My rational was it didn't make any sense to give the last-output a real value before it had ever been activated.

Unfortunately, creating the above Node yields a NPE; apparently because it's attempting to cast the nil as a double:

(->Node 0 nil)
NullPointerException   clojure.lang.RT.doubleCast (RT.java:1298)

If I remove the type-hint on last-output, it works fine.

I'm trying to get into the habit of type-hinting what I can. Is there a way to provide the type-hint on last-output, but also indicate that nil is an acceptable value?


Solution

  • If you really need to typehint with a primitive type, and using object type Double is not an option (like for performance related reason), the common practice (as far as i know) is to create your own custom constructor function:

    user> (defrecord Node [^double input-sum ^double last-output])
    user.Node
    
    user> (defn make-node [^Double input-sum ^Double last-output]
            (->Node (or input-sum 0) (or last-output 0)))
    #'user/make-node
    
    user> (make-node nil 0)
    #user.Node{:input-sum 0.0, :last-output 0.0}
    
    user> (make-node 10 nil)
    #user.Node{:input-sum 10.0, :last-output 0.0}
    
    user> (make-node 1 2)
    #user.Node{:input-sum 1.0, :last-output 2.0}
    

    the approach is used for all the cases, where you need some sophisticated logic for the entity construction.

    update

    If you really need both a primitive value and the way to distinguish value from "no value", you could use NaN for that:

    user> (defn make-node [^Double input-sum ^Double last-output]
            (->Node (or input-sum Double/NaN) (or last-output Double/NaN)))
    #'user/make-node
    
    user> (make-node 1 nil)
    #user.Node{:input-sum 1.0, :last-output NaN}
    

    so, it is value, and also is the primitive analogue of nil.