phrase
phrase copied to clipboard
Confused about phrasing a `coll-of`
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 ?
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.
The following upstream issue applies: https://dev.clojure.org/jira/browse/CLJ-2168
Thanks @alexanderkiel, I appreciate your detailed explanation!