Skip to content
This repository has been archived by the owner on Nov 11, 2018. It is now read-only.

A demo for Automatic Annotation inference for Typed Clojure

License

Notifications You must be signed in to change notification settings

typedclojure/auto-annotation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Write Tests, Get Types!

A demo for Typed Clojure's runtime type inference.

Overview

Run your tests, and clojure.core.typed/runtime-infer will insert your annotations.

Quick start

  1. Add these injections to project.clj

      :injections [(require 'clojure.core.typed)
                   (clojure.core.typed/install
                     #{:load})])
  2. Add this metadata to your ns form.

    (ns my-ns
      {:lang :core.typed
       :core.typed {:features #{:runtime-infer}}}
      (:require [clojure.repl :as r]))
  3. Run your tests from the REPL.

  4. Use clojure.core.typed/runtime-infer to generate core.typed types in the current namespace.

  5. use clojure.core.typed/spec-infer to generate clojure.spec specs in the current namespace.

  6. Use clojure.core.typed/refresh-runtime-infer to wipe inference results (perhaps from running bad tests).

Verbose

What follows:

  • 3 examples of inferring types
  • instructions on how to try this on your own code

Example 1

Let's infer the annotations in runtime-infer-demo.core.

First, we run its unit tests.

user=> (require 'runtime-infer-demo.core-test)
nil
user=> (in-ns 'runtime-infer-demo.core-test)
#object[clojure.lang.Namespace 0x193501b1 "runtime-infer-demo.core-test"]
runtime-infer-demo.core-test=> (run-tests)

Testing runtime-infer-demo.core-test

Ran 1 tests containing 2 assertions.
0 failures, 0 errors.
{:test 1, :pass 2, :fail 0, :error 0, :type :summary}

Then we use the (runtime-infer) function call to insert annotations directly in the file.

runtime-infer-demo.core-test=> (in-ns 'runtime-infer-demo.core)
#object[clojure.lang.Namespace 0x17e656a9 "runtime-infer-demo.core"]
runtime-infer-demo.core=> (t/runtime-infer)
nil

We can then type check this file by changing the ns form to:

(ns runtime-infer-demo.core
  {:lang :core.typed}
  (:require [clojure.core.typed :as t]))

and simply reloading the file in our REPL:

runtime-infer-demo.core=> (require 'runtime-infer-demo.core :reload)
nil

It's really type checking the code, try and invoke a null-pointer with (inc nil) (by inserting and saving the file) and notice we get a compile-time error!

runtime-infer-demo.core=> (require 'runtime-infer-demo.core :reload)
Type Error (runtime_infer_demo/core.clj:23:1) Static method clojure.lang.Numbers/inc could not be applied to arguments:


Domains:
  Number

Arguments:
  nil

Ranges:
  Number


in: (clojure.lang.Numbers/inc nil)



ExceptionInfo Type Checker: Found 1 error  clojure.core/ex-info (core.clj:4724)

Example 2

user=> (require 'runtime-infer-demo.server-port-test)
nil
user=> (in-ns 'runtime-infer-demo.server-port-test)
#object[clojure.lang.Namespace 0x225b5fc9 "runtime-infer-demo.server-port-test"]
runtime-infer-demo.server-port-test=> (run-tests)

Testing runtime-infer-demo.server-port-test

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
{:test 1, :pass 1, :fail 0, :error 0, :type :summary}
runtime-infer-demo.server-port-test=> (in-ns 'runtime-infer-demo.server-port)
#object[clojure.lang.Namespace 0x138e38b6 "runtime-infer-demo.server-port"]
runtime-infer-demo.server-port=> (t/runtime-infer)
nil

Now change the ns form to:

(ns runtime-infer-demo.server-port
  {:lang :core.typed}
  (:require [clojure.core.typed :as t]))

It should type check:

runtime-infer-demo.server-port=> (require 'runtime-infer-demo.server-port :reload)
nil

Example 3

runtime-infer-demo.server-port=> (require 'runtime-infer-demo.mini-occ)
WARNING: Removing :no-check from var clojure.pprint/pprint
WARNING: Duplicate var annotation:  clojure.pprint/pprint
Instrumenting clojure.pprint/pprint in runtime-infer-demo.mini-occ
nil
runtime-infer-demo.server-port=> (in-ns 'runtime-infer-demo.mini-occ)
#object[clojure.lang.Namespace 0x5f2b44ec "runtime-infer-demo.mini-occ"]
runtime-infer-demo.mini-occ=> (clojure.test/run-
clojure.test/run-all-tests   
clojure.test/run-tests       
runtime-infer-demo.mini-occ=> (clojure.test/run-tests)

Testing runtime-infer-demo.mini-occ
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo
foo

Ran 5 tests containing 19 assertions.
0 failures, 0 errors.
{:test 5, :pass 19, :fail 0, :error 0, :type :summary}

Now generate the types:

runtime-infer-demo.mini-occ=> (t/runtime-infer)
nil

Check out runtime-infer-demo.mini-occ for the output. These won't type check as-is, but they could be used to guide the final annotations (right now they're pretty ugly).

Usage

See the project.clj for the needed configuration. The code in :injections must be added before your code is run (it monkey patches load).

To infer types for your namespace, add this metadata to your namespace (like in runtime-infer-demo.core):

(ns runtime-infer-demo.core
  {:lang :core.typed
   :core.typed {:features #{:runtime-infer}}}
  (:require [clojure.core.typed :as t]))

Now run your unit tests, and call (t/runtime-infer) in your namespace. Allow your editor to reload your file (eg. in vim use the :edit command), and notice your files will have annotations.

License

Copyright © 2016 Ambrose Bonnaire-Sergeant

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

About

A demo for Automatic Annotation inference for Typed Clojure

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published