phrase icon indicating copy to clipboard operation
phrase copied to clipboard

Confused about phrasing a `coll-of`

Open marco-m opened this issue 6 years ago • 3 comments

Hello, I am missing how to phrase a coll-of. Consider the following:

(ns grape.phrase-test
  (:require [clojure.test :refer [deftest is are testing]]
            [clojure.spec.alpha :as s]
            [phrase.alpha :as phrase]))

(s/def ::just-int int?)

(phrase/defphraser int? [_ _] "Expected an integer (1)")

; this passes
(deftest simplest-possible-spec-for-phrase
  (is (= "val: 1.0 fails spec: :grape.phrase-test/just-int predicate: int?\n"
         (s/explain-str ::just-int 1.0)))
  (is (= "Expected an integer (1)"
         (phrase/phrase-first {} ::just-int 1.0))))

(s/def ::coll-of-ints (s/coll-of int?))

(phrase/defphraser coll? [_ _] "Expected a collection (2)")

(deftest coll-of-ints-fail-pred-int
  ; this passes
  (is (= "In: [0] val: :a fails spec: :grape.phrase-test/coll-of-ints predicate: int?\n"
         (s/explain-str ::coll-of-ints [:a])))
  ; this fails with `nil`
  (is (= "Expected an integer (1)" (phrase/phrase-first {} ::coll-of-ints [:a]))))

; this passes
(deftest coll-of-ints-fail-pred-coll
  (is (= "val: :a fails spec: :grape.phrase-test/coll-of-ints predicate: coll?\n"
         (s/explain-str ::coll-of-ints :a)))
  (is (= "Expected a collection (2)" (phrase/phrase-first {} ::coll-of-ints :a))))

Tests simplest-possible-spec-for-phrase and coll-of-ints-fail-pred-coll pass as expected. On the other hand, test coll-of-ints-fail-pred-int fails, phrase-first returns nil instead of "Expected an integer (1)". What am I missing ?

marco-m avatar Jul 17 '18 16:07 marco-m

In such situations, it's always a good idea to define a default phrase which simply returns the problem:

(defphraser :default
  [_ problem]
  problem)

with that, (phrase/phrase-first {} ::coll-of-ints [:a]) returns:

{:path [],
 :pred int?,
 :val :a,
 :via [:grape/coll-of-ints],
 :in [0],
 :phrase.alpha/mappings {},
 :phrase.alpha/via ()}

here you can see that the :pred is only int? without a namespace. Your phraser on the other hand is registered with the predicate clojure.core/int?. Thats not the same.

Here we have a spec bug. Spec should use clojure.core/int? as predicted and not simply int?. They used to use simple symbols of press everywhere in the past. I thing they just forgot to add the namespace here.

A workaround is to use a registered spec like your ::just-int in s/coll-of like so:

(s/def ::coll-of-ints (s/coll-of ::just-int))

I'll look for an existing or open an issue in the Clojure JIRA. This issue stays open until upstream is fixed.

alexanderkiel avatar Jul 18 '18 12:07 alexanderkiel

The following upstream issue applies: https://dev.clojure.org/jira/browse/CLJ-2168

alexanderkiel avatar Jul 18 '18 12:07 alexanderkiel

Thanks @alexanderkiel, I appreciate your detailed explanation!

marco-m avatar Jul 18 '18 14:07 marco-m