Adding Syntactic Sugar to Make Prismatic Schema Look Like Core.Typed/Haskell

92 views Asked by At
  1. Consider the way core.typed annotates a function:

    (t/ann typed-function [t/Str :-> t/Str])
    
  2. Now consider the way Prismatic Schema annotates a function:

    (s/defn schema-function :- s/Str
           [arg :- s/Str]
           arg)
    

Personally, I find the way that core.typed annotates a function be much clearer and closer in spirit to strongly typed languages like Haskell.

Question: Is there a way to make some sort of macro or function in clojure with Prismatic Schema that has the effect of (2) but the visual appearance of (1)? That is, something like the following:'

(custom-annotation-macro schema-function [s/Str :-> s/Str])
(defn schema-function [arg] arg)

such that the effect is merely just

(s/defn schema-function :- s/Str
      [arg :- s/Str]
       arg)
1

There are 1 answers

0
Cactus On

To illustrate how you could solve this with two macros:

(def type-annots (atom (hash-map)))

(defn add-type-annot [fn-name ty]
  (swap! type-annots #(conj % [fn-name ty])))

(defmacro custom-annotation-macro [fn-name ty]
  (add-type-annot fn-name ty)
  nil)

(defn split-fun-type [ty]
  ;; You will need to write this one; 
  ;; it should split [a :-> b :-> c] to [[a b] c]
  ['[s/Str s/Int] 's/Str])

(defmacro defn-typed [fn-name args & body]
  (let [ty (get @type-annots fn-name)]
    (if ty
      (let [[arg-types ret-ty] (split-fun-type ty)
            args-typed (apply vector (apply concat (map vector args arg-types)))]
        `(s/defn ~fn-name :- ~ret-ty ~args-typed ~@body))
      `(defn ~fn-name ~args ~@body))))

I haven't bothered to implement split-fun-type because I don't really know Clojure; the above is based on my understanding that it's a Lisp.