Response to your request on The REPL Podcast

26 views
Skip to first unread message

Nick Jones

unread,
Aug 12, 2019, 5:37:27 AM8/12/19
to core.typed
Hi Ambrose,

You made a request for people to reach out if people were using Typed Clojure or followed the project so I thought I'd respond. For me personally Typed Clojure was one of the more exciting announcements over Clojures history. I'd love Clojure to have a Type system. Thank you for undertaking this exercise.

There is a lot I like about Clojure and I find it very easy to write but very dense and hard to grok later when reading it back. I found the type annotations added readability and some formal verification to my code that I really appreciated later on. I've been doing Clojure as my main hobby language since around 2010 and I still find it extremely difficult at times to read other peoples libraries. I often find I need to execute them to work out what data is flowing through them which isn't ideal.

Unlike the bulk of the Clojure community I actually don't see the REPL as the best tool in isolation for development (Don't get me wrong its still very useful). I can actually get more of a flow on with a Smart IDE and a language like F# or Java and their great static analysis toolkits.

Here is some general feedback

Likes:
  1. I found the error messages often very helpful. Much better for me personally than what spec generates.
  2. Per namespace is useful if you want to mix typed/untyped code.
  3. I actually like the function level annotations. I find languages that take type inference too far actually end up losing some of the added readabilty that types can provide. I think if you can define your types at the domain and function level only thats a happy place for me.
Workons:
  1. The documentation is a bit scattered and at times I found it hard to work out how to apply the annotations correctly. It could really do with a refresh to give people a fresh view of where the project is at. I actually got a few tips from the Rock/Scissors/paper demo that Alex Miller wrote up years ago. I think more demos on how to type a real world use case would be very helpful.  
Questions and the Future:
  1. I am a member of Clojurists together and I mentioned this project in a recent survey as one that I would like to see funded further. Perhaps you should look at that.
  2. What do you use as you editor and tooling? Colin Fleming provided the ability to type check a namespace in Cursive that I found very useful. I'm wondering if you use and blessed editor setup?
  3. Keep up the good work here and I hope you decide to pursue taking this project forward.
I just thought I'd include this little snippet below from my notes when I was playing around with the types in Clojure.

(ns nz.co.arachnid.shadespire.domain
 
(:require [clojure.core.typed :as t])
 
(:require [nz.co.arachnid.shadespire.util :as util]))

;; =======================================
;;              Type Aliases
;; =======================================

(t/defalias WarBand (t/U (t/Value :nz.co.arachnid.shadespire.domain/liberators)
                         
(t/Value :nz.co.arachnid.shadespire.domain/bloodreavers)))

(t/defalias AttackDiceFace (t/U (t/Value :nz.co.arachnid.shadespire.domain/attack_dice_hammer)
                               
(t/Value :nz.co.arachnid.shadespire.domain/attack_dice_critical)
                               
(t/Value :nz.co.arachnid.shadespire.domain/attack_dice_sword)
                               
(t/Value :nz.co.arachnid.shadespire.domain/attack_dice_halfmoon)
                               
(t/Value :nz.co.arachnid.shadespire.domain/attack_dice_fullmoon)))

(t/defalias DefenceDiceFace (t/U (t/Value :nz.co.arachnid.shadespire.domain/defence_dice_sheild)
                                 
(t/Value :nz.co.arachnid.shadespire.domain/defence_dice_dodge)
                                 
(t/Value :nz.co.arachnid.shadespire.domain/defence_dice_critical)
                                 
(t/Value :nz.co.arachnid.shadespire.domain/defence_dice_halfmoon)
                                 
(t/Value :nz.co.arachnid.shadespire.domain/defence_dice_fullmoon)))

(t/defalias AttackDice (t/HVec [AttackDiceFace AttackDiceFace AttackDiceFace AttackDiceFace AttackDiceFace AttackDiceFace]))
(t/defalias DefenceDice (t/HVec [DefenceDiceFace DefenceDiceFace DefenceDiceFace DefenceDiceFace DefenceDiceFace DefenceDiceFace]))

(t/defalias RollOffResult (t/U (t/Value :nz.co.arachnid.shadespire.domain/attack_success)
                               
(t/Value :nz.co.arachnid.shadespire.domain/defence_success)
                               
(t/Value :nz.co.arachnid.shadespire.domain/draw)))

(t/defalias RollOffPredicate (t/IFn [(t/U AttackDiceFace DefenceDiceFace) -> t/Bool]))

(t/ann ATTACK_DICE AttackDice)
(def ATTACK_DICE [::attack_dice_hammer
                 
::attack_dice_hammer
                 
::attack_dice_sword
                 
::attack_dice_critical
                 
::attack_dice_halfmoon
                 
::attack_dice_fullmoon])

(t/ann DEFENCE_DICE DefenceDice)
(def DEFENCE_DICE [::defence_dice_sheild
                   
::defence_dice_sheild
                   
::defence_dice_critical
                   
::defence_dice_dodge
                   
::defence_dice_fullmoon
                   
::defence_dice_halfmoon])

;; =======================================
;;              Functions
;; =======================================

(t/ann is-critical? [(t/U AttackDiceFace DefenceDiceFace) -> t/Bool])
(defn is-critical?
 
[roll]
 
(or (= roll ::attack_dice_critical)
     
(= roll ::defence_dice_critical)))


;; NJ_TODO See if we can correlate these types further. I.e. if we are passed an Attack Dice we can only return a Vector of AttackDice
(t/ann roll-dice [(t/U DefenceDice AttackDice) t/Int -> (t/U nil (t/ASeq (t/U AttackDiceFace DefenceDiceFace)))])
(defn roll-dice
 
[dice num-dice]
 
(for [_ (range 0 num-dice)]
   
(first (shuffle dice))))


(t/ann rolloff [(t/AVec AttackDiceFace) (t/AVec DefenceDiceFace) RollOffPredicate RollOffPredicate -> RollOffResult])
(defn rolloff
 
[attackDice, defenceDice, fnAttackSuccess, fnDefenceSuccess]
 
(let [attacker_success_count (count (filter fnAttackSuccess attackDice))
        defence_success_count  
(count (filter fnDefenceSuccess defenceDice))]
   
(cond
     
(> attacker_success_count defence_success_count) ::attack_success
     
(> defence_success_count attacker_success_count) ::defence_success
     
:default                                         ::draw)))

(t/ann critical-rolloff [(t/AVec AttackDiceFace) (t/AVec DefenceDiceFace) -> RollOffResult])
(defn critical-rolloff
 
[attackDice defenceDice]
 
(rolloff attackDice defenceDice is-critical? is-critical?))




Reply all
Reply to author
Forward
0 new messages