Fork me on GitHub
#clojure-spec
<
2016-12-08
>
artemyarulin09:12:03

Hello, just started to play a bit with spec. Is there any way to make it shorter? Can I somehow embed ::first-name inside ::person without separate def?

(s/def ::first-name string?)
(s/def ::last-name string?)
(s/def ::person (s/keys :req [::first-name ::last-name]))

patrkris10:12:47

@artemyarulin I don't think there is

artemyarulin10:12:08

hm, ok. It kinda forces me to design everything bottom to the top

dominicm10:12:46

I think that's on purpose

thegeez14:12:16

(s/conform (s/cat :pre (s/& (s/+ #{1 2 3})
                              (fn [r]
                                (if (= r [1 2 3])
                                  r
                                  ::s/invalid)))
                    :keyword #{:a})
             [1 2 3 :a])

=> {:pre [1 2 3], :keyword :a}

thegeez14:12:25

For the :pre match I want to match the sequence 1 2 3 in that order as the start of a larger sequence. Is there an easier way to write the spec for the :pre part? I tried stuff like #{[1 2 3]}

Alex Miller (Clojure team)14:12:33

(s/cat :1 #{1} :2 #{2} :3 #{3})

Alex Miller (Clojure team)14:12:56

Or similar with s/tuple

thegeez14:12:06

But s/tuple can't be used within a regex, right?

thegeez14:12:53

I went with:

thegeez14:12:04

(defmacro match-seq [c]
  (let [ks (vec (mapv keyword (repeatedly (count c) gensym)))
        cat-kvs (mapcat vector ks (map hash-set c))]
    `(s/with-gen
       (s/&
        (s/cat [email protected])
        (s/conformer
         (juxt [email protected])
         (fn [out#]
           (zipmap ~ks out#))))
       (fn []
           (s/gen #{~c})))))

  (s/conform (s/cat :pre (match-seq [1 2 3])
                    :keyword #{:a})
             [1 2 3 :a])
;=> {:pre [1 2 3], :keyword :a}

bbloom18:12:09

just remembered an old happening from the .net world that I thought the folks here may be interested in: https://blogs.msdn.microsoft.com/kathykam/2007/03/29/bye-bye-system-timezone2-hello-system-timezoneinfo/

bbloom18:12:38

in short, System.TimeZone was broken in a bunch of ways, so they made System.TimeZone2, but people freaked out about that name, so they caved and called it TimeZoneInfo.... leading to substantial confusion

gfredericks18:12:52

> using numeric modifier is not scalable in the long term

gfredericks18:12:10

eventually the numbers just get too big

gfredericks18:12:27

mathematicians tell us that there is no limit to how big numbers can get

eraserhd18:12:22

I wonder what the smallest number which isn't representable with the amount of information present in the universe is.

bbloom18:12:08

knowing that number would change the amount of information in the world, hence changing that number

eraserhd18:12:27

Well, I obviously couldn't know it.

bfabry18:12:03

I remember watching a ted talk on this, it's interesting. but yeah, what's to stop me saying "the biggest number the universe can represent, times two"

eraserhd18:12:34

Right, that's a representation.

bfabry18:12:35

we could just redefine 1 to be "the biggest number the universe can represent" 😛

bbloom18:12:52

bfabry: the trick is not to abstract over numbers, but to abstract over the operations which generate those numbers

gfredericks18:12:01

use BB(11111), as scott aaronson suggests

eraserhd18:12:20

@bfabry If I could find the number by an algorithm, the algorithm would have to require more information to represent than present in the universe to represent.

gfredericks18:12:43

BB(n) exposes an ambiguity in the question, I suppose -- if a number is platonically well-defined but can't be computed, does that count?

eraserhd18:12:10

Interesting

eraserhd18:12:18

I just got to that part

gfredericks18:12:37

the fun part is I don't even know what I mean by "platonically well-defined"; it's just an emotion

bbloom18:12:55

as fun as i find numbers, i was hoping somebody had some insights in to the hardest problem in computer science: the paucity of names 🙂

gfredericks19:12:01

@bbloom what do you think of the renaming you linked to?

bbloom19:12:37

honestly, i liked the name TimeZone2 - it means i don’t need to look at the docs for TimeZone or TimeZoneInfo to know which does what

bbloom19:12:40

basically i just ignore TimeZone

bbloom19:12:04

i can understand the desire to not have to write TimeZone2 in places

gfredericks19:12:15

because it's ugly?

gfredericks19:12:35

you feel like you have to memorize a version number for every class/API you learn to use?

bbloom19:12:45

yeah, but specifically it’s ugly for an ugly reason: it showcases a mistake

bbloom19:12:56

it’s not my mistake, but there’s still a human error as an accident of history in my code

bbloom19:12:05

now, that’s fine, b/c everything is human errors and accidents of history

bbloom19:12:19

my next thought is: ok, so let’s use aliases, which is what rich suggested too

bbloom19:12:25

but i’m not so sure that’s a good solution either

gfredericks19:12:38

ns aliases in particular? :rename too I guess

bbloom19:12:48

yeah, those combined

bbloom19:12:52

or even just a simple def

bbloom19:12:13

that helps for migration, which is key, but it still kinda sucks b/c of the nature of context

bbloom19:12:32

if you and i have a conversation about TimeZone, how do you know which version i mean?

gfredericks19:12:55

yeah there'll always be a version number at one of the levels

bbloom19:12:59

by utilizing rename/alias, you wind up in the same place you started with if you had used semver or whatever (shudder)

gfredericks19:12:11

function name, ns name, artifact name, ecosystem name...

bbloom19:12:23

yeah, well ecosystem name is the easy one

bbloom19:12:27

you just invent a brand name and you’re good

gfredericks19:12:41

every 5-10 years we declare bankruptcy on all this and start a new programming language

bbloom19:12:50

like instead of Clojure 2.0 you just fucking call it Twojure or something and you can change whateve ryou want, but at the cost of bifurcating the community

gfredericks19:12:06

and then everybody rushes in to write the first http library

bbloom19:12:32

here’s where i’d use a /giphy lion king circle of life

gfredericks19:12:04

I don't think there's any Super Happy approach to this :(

bbloom19:12:57

i’d settle for a reasonable principal by which i can rationalize my decisions and remain mostly happy that i didn’t LOSE a battle with the laws of nature

bbloom19:12:27

alias & rename give us some powerful tools

bbloom19:12:33

but i feel like something is missing

bbloom19:12:45

one problem i’ve run in to a bunch is the impl vs interface namespace problem

bbloom19:12:58

spec sorta has this problem in that it has some public macros that it’s like “don’t use me directly please"

bbloom19:12:14

and then there’s core.async that has the impl namespaces and then redefs an indirection to export all the functionss

bbloom19:12:38

i wonder if there was some way to abstract over aliases and renames that you could more easily address this

bbloom19:12:50

not sure what that would look like

bbloom19:12:18

but if we’re going to use names as a tool to achieve accretion (and i see no other way, since everything in symbolic reasoning involves names), then i think we need more tools for names

bbloom19:12:51

that’s as far as i’ve managed to think here </rant>

gfredericks19:12:04

rich wanted a global mapping from namespaces to maven artifacts

gfredericks19:12:11

or seemed to at least

gfredericks19:12:17

this is sort of a different topic

bbloom19:12:13

he seemed to be making two points: 1) we’ve got bad abstractions: project files, version numbers, etc and 2) accrete, don’t break, use names to do that

bbloom19:12:05

if it were up to me, we’d abolish artifact ids etc as much as possible - namespaces have much nicer properties

gfredericks19:12:23

just publish individual namespaces? with versions?

bbloom19:12:25

you could probably define some information-preserving algebra of them. ie merge two sets of names, aliases, etc

bbloom19:12:20

yeah, i think so

bbloom19:12:05

maybe published dates instead of versions, since we’re turning version trees in to sequences

gfredericks19:12:24

he said the useful thing about artifacts is that they intentionally tie together particular versions of particular namespaces

bbloom19:12:47

i mean do they tho? only if you bundle your deps

niquola19:12:00

Hi all. I need spec for map such as: if key - some pred, then value - spec1, else if key - other pred, then value - spec2?

gfredericks19:12:24

@bbloom I mean if your artifact contains multiple namespaces, which it probably does, they are free to assume things about each other because you know that you'll get exactly the same version of all of them

bbloom19:12:18

@gfredericks ah, i understand what you’re saying. yeah, in that sense those are actually tied together and tested together. i think that’s not incompatible with the idea of “eliminating” artifacts or projects tho… you'd just use the context of a project in resolving versions of namespaces, ideally in some content addressable way maybe?

bbloom19:12:26

so if i have namespace foo and i require foo.bar, then i have a project that says “hey, i’m going to publish namespaces #”foo.*”, then when those get published they get tagged with some artifact bundle in some way, but i don’t actually think about that, i think about the version of the nodes in that dependency graph i point to directly

bbloom19:12:58

so you resolve internal dependencies within a project using immutable references, like hashes, and then you resolve external dependencies using lamport logic

bbloom19:12:03

eg by intersecting date ranges

gfredericks19:12:56

If backwards compatibility is reliable presumably your date range can be open on the right side and intersections are trivial

bbloom19:12:19

right, but of course people make mistakes, so you’d probably need two features:

bbloom19:12:28

1) right bound to say “i no longer trust this maintainer"

bbloom19:12:34

and 2) a black list of bad versions

bbloom19:12:41

so your timelines could have right bounds or holes

gfredericks19:12:19

If intersections are empty then the build tool reports a failure?

bbloom19:12:52

and you always select the most recent version from the intersection

bbloom19:12:15

then layer on top the standard dependency pinning behavior for prod

bbloom19:12:11

i want this. somebody make this.

gfredericks19:12:36

It can be a first-class feature of twojure

bbloom19:12:36

alex probably wouldn’t tell us if rich was working on a new tool here 😉

bbloom19:12:57

Twojure has a nice ring to it

dspiteself19:12:16

It seemed their primary goal was running the minimal set of tests and generative tests for a given change.

dspiteself19:12:03

here is to hoping they will continue the https://github.com/Datomic/codeq thought

niquola21:12:23

I was able to spec my map with

(s/coll-of (s/or :option1 (s/tuple key-spec-1 val-spec-1) ....))
, but path in error messages is not informative: [0 1 0 1]

nwjsmith21:12:43

:thinking_face: I’m having trouble supplying generator overrides

(ns scratch
  (:require [clojure.spec :as s]
            [clojure.spec.gen :as gen]))

(s/def ::id (s/or :tempid int? :uuid uuid?))
(s/def :thing/id ::id)
(s/def :thing/entity (s/keys :req [:thing/id]))

(gen/sample (s/gen :thing/entity
                   {:thing/id #(gen/int)}))

nwjsmith21:12:34

I would expect that call to gen/sample to give me :thing/entitys with int values for the :thing/id key.

nwjsmith21:12:13

Instead, it’s as if I hadn’t supplied the override at all. I get a mix of int and uuids.

hiredman21:12:26

::id makes it work

hiredman21:12:16

(dunno what the expected behavior is, if that is a bug, etc)

hiredman21:12:29

::id in the override map

nwjsmith21:12:54

Yeah, it does. This is a minimized example, but I’d like to keep from overriding all ::ids, and just override thing/entity‘s

nwjsmith22:12:25

@hiredman thanks, I think your suggestion help me identify my problem. I’m pretty sure it’s a bug

nwjsmith22:12:33

(ns scratch
  (:require [clojure.spec :as s]
            [clojure.spec.gen :as gen]))

(s/def ::number number?)
(s/def ::numeric ::number)
(gen/sample (s/gen ::numeric
                   {::numeric gen/int}))

nwjsmith22:12:15

I would expect that gen/sample call to only return ints, but it returns ints and floats instead

nwjsmith22:12:43

I think that this means that you can’t override “aliased” specs, which is a bug?

gfredericks22:12:56

I hope it's a bug

bfabry22:12:45

probably raise a jira, that behaviour is really surprising

boot.user=> (s/def ::numeric ::number)
:boot.user/numeric
boot.user=> (s/def ::numeric-coll (s/coll-of ::number))
:boot.user/numeric-coll
boot.user=> (gen/sample (s/gen ::numeric {::number gen/int}))
(-1 1.0 -1 NaN -1 2.5625 -1.5 -47 -13 24)
boot.user=> (gen/sample (s/gen ::numeric-coll {::number gen/int}))
([0 0 0 0 0 0 0 0 0] [-1 1 0 -1 -1 1 0] [-1 -2 1 -1 2 -2] [-2 -1 -2 -2 3 -2 1 -3] [-1 1 4 3 -4 4 -1 2 -4 -2 -4 4 -2 -4 0 -1] [1 -4 0 -1 0 -2 -1 -4 -2 -3 5 0 3 -1 -1 -3 -3 -1 -4] [3 -2 4 -3 -6 4 -3 3 2] [-7 -7 -1 -1 3 6 0 -7 0 -3 1] [] [9])

nwjsmith22:12:30

Filing now, thanks!