Fork me on GitHub
#clojure-spec
<
2018-02-18
>
mathpunk01:02:27

I'm writing my first regular expression of specs. Both these tests should pass:

mathpunk01:02:36

A :sherman.grammar/expanding-term should contain ONE or more :sherman.grammar/expanding-symbols, and ZERO or more :sherman.grammar/terminating-symbols

mathpunk01:02:54

I thought this might be it:

mathpunk01:02:43

At least, I thought that would handle "This is #going# #to# #expand# maybe"

mathpunk01:02:50

which isn't fully what I'm after but would be partway

taylor01:02:50

have you tried s/alt

mathpunk01:02:56

I diiiid but not very well

mathpunk01:02:38

Come to think of it I don't know that my input data is regular

mathpunk01:02:24

I'm trying to express, "A string that, if you split it at the spaces, one or more of the elements of that collection would be an ::expanding-symbol"

mathpunk01:02:47

Maybe I should write a function that tests that, and uses it as the predicate

mathpunk01:02:16

I wanted to check here to see if there is a way to alt this intent

taylor01:02:30

seems like s/cat works for your case unless I’m missing something

taylor01:02:38

oh wait, you want your inputs to be actual strings?

noisesmith01:02:14

s/alt could spec the result of splitting the string at spaces

taylor01:02:23

in that case, I think I’d tokenize the string before spec’ing it

mathpunk01:02:19

Getting there. I'm incorrect about what s/+ means, though:

mathpunk02:02:10

I've changed my terminology a little, but I'm still failing to reject invalid input:

mathpunk02:02:48

I hypothesize that the right thing to do is to make functions do more work, and specs do less. E.g., much as noisesmith suggested when they suggested tokenizing first, I could have a predicate which detects well-formed tokens, and base a spec off of that

mathpunk02:02:09

I'm still working out what responsibilities belong in spec and what belong in plain ol' clojure code 🙂

taylor03:02:58

@mathpunk another option using s/&:

(s/def ::valid-term
  (s/& (s/* (s/alt :terminating ::terminating-symbol
                   :expanding ::expanding-symbol))
       #(some (comp #{:expanding} first) %)))
(s/conform ::valid-term (tokenize "This #expands# also"))
=> [[:terminating "This"] [:expanding "#expands#"] [:terminating "also"]]

seancorfield03:02:37

I'll channel Alex Miller and say clojure.spec is not intended for string parsing -- there are much better tools out that for that (e.g., instaparse).

seancorfield03:02:27

The "regular expression" part of spec is intended for sequence processing.

mathpunk03:02:52

I've wondered exactly this!: whether instaparse was obsolete after spec

aengelberg03:02:05

Come join us

aengelberg03:02:08

We have cookies

aengelberg03:02:39

And somewhat lackluster but existent respond rate to issues

bmaddy04:02:35

I would expect clojure.spec.alpha/and to be commutative. Is anyone else seeing this behavior?

> *clojurescript-version*
"\"1.9.946\""
> (defn f [m] (println m) (-> m (get 1) :db/id (= 1)))
"#'invest-calc.state/f"
> (s/def :account/works (s/and f (s/map-of any? (s/keys))))
":account/works"
> (s/def :account/fails (s/and (s/map-of any? (s/keys)) f))
":account/fails"
> (s/conform :account/works {1 {:db/id 1}})
{1 #:db{:id 1}}
"{1 #:db{:id [:db/id 1]}}"
> (s/conform :account/fails {1 {:db/id 1}})
{1 #:db{:id [:db/id 1]}}
":cljs.spec.alpha/invalid"

Alex Miller (Clojure team)04:02:18

s/and flows conformed results through predicates

bmaddy04:02:30

Heh, I guess I should read the docs closer. Is it expected that it would operate differently in normal Clojure?

invest-calc.state> *clojure-version*
{:major 1, :minor 9, :incremental 0, :qualifier nil}
invest-calc.state> (defn f [m] (println m) (-> m (get 1) :db/id (= 1)))
#'invest-calc.state/f
invest-calc.state> (s/def :account/works (s/and f (s/map-of any? (s/keys))))
:account/works
invest-calc.state> (s/def :account/fails (s/and (s/map-of any? (s/keys)) f))
:account/fails
invest-calc.state> (s/conform :account/works {1 {:db/id 1}})
{1 #:db{:id 1}}
{1 #:db{:id 1}}
invest-calc.state> (s/conform :account/fails {1 {:db/id 1}})
{1 #:db{:id 1}}
{1 #:db{:id 1}}