Type hints in Clojure have a different purpose to those in Python. In Clojure, 
type hints are only a mechanism to avoid reflection; their use is solely to 
improve performance.

So the question "Can I use spec as a type hint?" is actually asking "Will the 
compiler use specs to avoid reflection?", to which the answer is no. However 
Spec, and other equivalent libraries, can be used for runtime type checking in 
Clojure.

Where you'd use a dataclass in Python, you'd generally use a map in Clojure. 
This requires a little explanation because of the different ways Clojure and 
Python support data modelling.

Suppose Python you have a dataclass:

@dataclass
class InventoryItem:
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

Then the *minimal* equivalent Clojure code is simply:

(defn total-cost [{:keys [unit-price quantity-on-hand]}]
  (* unit-price quantity-on-hand))

The Clojure example lacks explicit type checking, but that may not be 
necessary. In Clojure, we can incrementally add more specific checks as 
necessary. For example, we could add a precondition to check the inputs:

(defn total-cost [{:keys [unit-price quantity-on-hand]}]
  {:pre [(float? unit-price) (int? quantity-on-hand)]}
  (* unit-price quantity-on-hand))

Clojure spec allows checks to be taken further, with the caveat that keywords 
must be namespaced. Spec is more granular than classes, as it's interested in 
single key/value pairs, rather than grouped properties as in a class or struct. 
With spec, we might declare:

(s/def :inventory.item/name string?)
(s/def :inventory.item/unit-price float?)
(s/def :inventory.item/quantity-on-hand int?)

(defn total-cost [{:inventory.item/keys [unit-price quantity-on-hand]}]
  (* unit-price quantity-on-hand))

This doesn't automatically perform type checks, as they may not be necessary. 
We can add type checks to a function with *clojure.spec.test.alpha/instrument*, 
or add them as a precondition using *clojure.spec.alpha/valid?*.

The validation required depends on the origin of the data. Suppose the 
inventory item comes from a database with its own schema. In which case, we may 
be reasonably certain that the types are correct and there's no need for 
additional confirmation.

Or suppose instead that we read the inventory item from an external source we 
may not trust. In that case, we'd want to validate it as input, and reject it 
if the data is invalid. But after we've validated it, we can pass it around 
internally without further checks.

Perhaps we're worried instead about human error. In this case, we might turn on 
checking during development and testing, but remove it during production when 
we're more interested in performance. This is the broad use-case for Spec's 
*instrument* function.

Clojure's takes a more nuanced, and possibly unique approach to data, compared 
to other languages. Understanding how Clojure views data is understanding 
Clojure as a language.

On Fri, 21 Jan 2022, at 3:22 AM, Kovas Palunas wrote:
> Hi all,
> 
> Coming from python, I use dataclasses 
> <https://docs.python.org/3/library/dataclasses.html> a lot to tie complex 
> collections of data together. I like using them in combination with type 
> hints so that it's clearer to me at a glance what kind of data a function is 
> processing.  When my data starts to look like a dict of dicts of lists, I 
> turn to dataclasses to help my simplify.
> 
> So far in my Clojure journey, I've stumbled across two features that could 
> help me do something similar.  First are datatypes 
> <https://clojure.org/reference/datatypes> (deftype, defrecord), and second is 
> using maps with spec <https://clojure.org/guides/spec>.  Based on my reading, 
> using spec to define types seems like a really flexible system that does 
> built in testing for me.  I was surprised to read that specs can't quite be 
> treated like types though: 
> https://ask.clojure.org/index.php/10464/can-i-use-spec-as-type-hint.  Maybe 
> the intention is to use the :pre and :post keys in a function's options as 
> type hints?
> 
> I'm curious if I'm thinking along the right lines here, or if there are other 
> language features I could be looking at.  Also curious if anyone has 
> suggestions for books or articles that explore data typing options in Clojure.
> 
> Thanks,
> 
>  - Kovas
> 
> 
> -- 
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with your 
> first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> --- 
> You received this message because you are subscribed to the Google Groups 
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to clojure+unsubscr...@googlegroups.com.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/clojure/a5718e8b-71a6-40d5-b1da-ca2cdb10a71en%40googlegroups.com
>  
> <https://groups.google.com/d/msgid/clojure/a5718e8b-71a6-40d5-b1da-ca2cdb10a71en%40googlegroups.com?utm_medium=email&utm_source=footer>.

-- 
James Reeves
booleanknot.com

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/clojure/da5de1bf-a466-4b78-898a-84d9b6217a53%40www.fastmail.com.

Reply via email to