
Show HN: js.spec, a JavaScript implementation of clojure.spec - prayerslayer
https://github.com/prayerslayer/js.spec
======
moxious
I think of dynamic typing in a language as a big pro and a big con at the same
time, but it's one of the things that makes a language like JavaScript or
closure what it is.

I love typing systems too, there are compelling advantages...and
disadvantages.

I guess sometimes what seems strange is attempts to bolt type systems on to
fundamentally dynamic languages, like adding two extra wheels to a motorcycle,
extra fairing, and then calling it a car.

I don't fault anyone for wanting a car. But if you wanted a car, why not start
off with a statically typed language? Why bolt this stuff on really in
contravention to what the dynamic languages are trying to be?

Perhaps this is JavaScript fatigue in a new guise? If everything has to be
JavaScript (for whatever reason) then having no static typing ever may be hard
to live with?

Am I missing something or does the motorcycle with two extra wheels seem
destined to be clunkier and less flexible than a proper car?

~~~
mhluongo
Spec (and more generally, contracts) aren't just about "making up" for the
lack of static typing. They can encode a variety of invariants that a type-
system can't, and are also useful for parsing (see coercion) and generative
testing. Unlike most type systems, they also aren't required across the whole
code base.

~~~
tel
Parsing and generative testing are well-covered by static types, but I'll give
you that contracts can enforcing things static types cannot. That said, static
types can enforce things that contracts cannot, too.

Furthermore, in my experience I find use of the things that static types can
catch and contracts cannot on a nearly hourly basis while I rarely to never
use things that contracts can check but static types cannot.

~~~
arohner
> static types can enforce things that contracts cannot

Example, please?

~~~
tel
Sure. Consider the type

    
    
        forall a. a
    

With mild conditions on what the program fragment achieving this type can do I
can be certain that this function does not return. It is an error, an infinite
loop, or simply does not exist.

No contract can verify this because they inspect the value and thus get stuck
by infinite loops.

I don't use that sort of thing often, but here's a simpler one:

    
    
        sort :: Ord a => [a] -> [a]
    

I know that `sort` only uses the ordering properties of the values within the
list. It could be generalized to `forall a . (a -> a -> Ordering) -> [a] ->
[a]` in which case it would be totally oblivious to the values inside of the
list except in that it probably applies that function.

No contract system can examine functions. No contract system can prove
universals. Types can do both (in certain, principled ways).

\---

Another interesting example is handling something like a channel. In Haskell,
we have the type `TChan a` which is a channel carrying values of type `a`. In
Clojure, the best we can do for giving a contract to a core.async chan is to
say it's a "chan containing something" or even "a dereffable thing returning
something". To use a contract here we'd have to check every value that ever
pops off the channel.

Contracts check shape, but cannot ensure usage.

------
rattray
Seems cool.

Also worth checking out: tcomb[0] which can let you use your Flow types at
runtime for eg; pattern matching.

[0] [https://github.com/gcanti/tcomb](https://github.com/gcanti/tcomb)

~~~
prayerslayer
Cool, thanks for sharing. I didn't know that one.

------
dwwoelfel
Have you used js.spec or clojure.spec in production? Has it helped you catch
bugs that you would have otherwise missed? How does the runtime-only
limitation work out in practice?

I know that flow has prevented me from checking in bugs, because I've tried to
push up code with e.g. misspelled property names and the pre-push hook stopped
me.

~~~
mhluongo
One of the Clojure validation libs predating spec, schema, has saved us from a
ton of errors, and works similarly. It also opens up reasoning about data
shape- eg, auto-generating test data based on schemas. spec's support for that
use case is even better.

EDIT: You can see schema in action in
[https://github.com/cardforcoin/shale](https://github.com/cardforcoin/shale).
In fact, the latest build surfaced a bug via schema-
[https://circleci.com/gh/cardforcoin/shale/379](https://circleci.com/gh/cardforcoin/shale/379).
Next version we're planning to migrate to spec.

