How do I annotate protocols and their methods in Clojure's core.typed?

353 views Asked by At

I am making a tic tac toe game and have made a protocol for my Strategy. The game is running fine, so I want to take this opportunity to hone my core.typed skills. I have annotated the protocol (as shown below), but when I run (cf method-name) or (cf protocol-name) in the repl, I get this error:

eg:

=> `(cf win)`
clojure.lang.ExceptionInfo: Type Checker: Found 1 error :: {:type-error :top-level-error, :errors (#<ExceptionInfo clojure.lang.ExceptionInfo: Unannotated var tic-tac-toe.protocol/win
Hint: Add the annotation for tic-tac-toe.protocol/win via check-ns or cf {:env {:file "/Users/jessediaz/Documents/workspace/tic-tac-toe/src/tic_tac_toe/protocol.clj", :column 5, :line 47}, :form win, :type-error :clojure.core.typed.errors/tc-error-parent}>)}

I have checked to make sure that the version of protocol is in fact core.typed/protocol. The console barked at me when I used protocol> saying the syntax was deprecated. I have also looked through the documentation on both the github page and clojure-doc.org. This is how I learned that there is an optional :methods keyword that I can use to map methods to types. I feel that this might remove a lot of repetition from my code, but I haven't been able to find any examples of its usage. the main strategy methods in the protocol all have side effects and return nil. The validation methods either return nil or the original Strategy methods that they validating. I am not sure I have the syntax right there, but the code is evaluating fine in LightTable, with or without the Fn signature, and they the core.typed wiki implies that it is not always necessary.

I feel like I am on the cusp here in learning this amazing library and would appreciate any insightful advice that could help my understanding.

protocol.clj file:

(ns tic-tac-toe.protocol
  (:require [clojure.core.typed :refer :all]))

(ann-protocol Strategy
                win                        (Fn [Strategy -> nil])
                block                      (Fn [Strategy -> nil])
                fork                       (Fn [Strategy -> nil])
                block-fork                 (Fn [Strategy -> nil])
                take-center                (Fn [Strategy -> nil])
                take-opposite-corner       (Fn [Strategy -> nil])
                take-corner                (Fn [Strategy -> nil])
                take-side                  (Fn  [Strategy -> nil])

                can-win?                   (Fn [Strategy -> (U nil (Fn [Strategy -> nil]))])
                can-block?                 (Fn [Strategy -> (U nil (Fn [Strategy -> nil]))])
                can-fork?                  (Fn [Strategy -> (U nil (Fn [Strategy -> nil]))])
                can-block-fork?            (Fn [Strategy -> (U nil (Fn [Strategy -> nil]))])
                can-take-center?           (Fn [Strategy -> (U nil (Fn [Strategy -> nil]))])
                can-take-corner?           (Fn [Strategy -> (U nil (Fn [Strategy -> nil]))])
                can-take-side?             (Fn [Strategy -> (U nil (Fn [Strategy -> nil]))]))
(defprotocol Strategy
  "Strategy methods update the Tic-tac-toe board when they are called"

  ;; strategies
  (win [_] "wins the game by filling an open space to get 3 in a row")
  (block [_] "blocks an opponents win by filling an open space")
  (fork [_] "creates a two way win scenario guaranteeing victory")
  (block-fork [_] "prevents an opponent from forking")
  (take-center [_] "takes center")
  (take-opposite-corner [_] "takes a corner opposite to one the computer already has")
  (take-corner [_] "takes an avaiable corner")
  (take-side [_] "takes an available side")

  ;; tests
  (can-win? [_])
  (can-block? [_])
  (can-fork? [_])
  (can-block-fork? [_])
  (can-take-center? [_])
  (can-take-opposite-corner? [_])
  (can-take-corner? [_])
  (can-take-side? [_]))
1

There are 1 answers

5
Ambrose On BEST ANSWER

I suspect you need to check-ns the current namespace before querying it with cf. Evaluation of typed code does not trigger type checking or annotation collection.

The ann-protocol is not needed here, clojure.core.typed/defprotocol supports inline annotation.

Try something like:

(defprotocol Strategy
  "Strategy methods update the Tic-tac-toe board when they are called"

  ;; strategies
  (win [_] :- nil "wins the game by filling an open space to get 3 in a row")
  (block [_] :- nil "blocks an opponents win by filling an open space")
  ...)