Giter Club home page Giter Club logo

fm's People

Contributors

localshred avatar usernolan avatar

Stargazers

 avatar

Watchers

 avatar

Forkers

localshred

fm's Issues

Convert positional args predicates/specs to s/cat catenations

See #6

We're adding automatic conversion from defm definitions to s/fdef so that we can pipe straight through to stest/check. Unfortunately fdef doesn't work with the style that fm allows providing a set of predicates in the same shape as the args list for a fn, so fm-form needs to rewrite the provided :fm/args argument list into an (s/cat ...) expression with the arg names tagged appropriately.

;; fm-form should turn this...
(defm test
  ^{:fm/args [int? [int? int?]]}
  [x [y z]]
  (+ x y z))

;; into this...
(defm test
  ^{:fm/args (s/cat :x int? (s/cat :y int? :z int?))}
  [x [y z]]
  (+ x y z))

We may also want to consider providing gen/gen-from-pred where available on incoming predicates.

Multiple signatures

fns can have multiple signatures:

(fn 
  ([x]   (inc x))
  ([x y] (inc (+ x y)))

It's easy enough to break this up between different fms, but it could be pretty straightforward to support the familiar syntax:

(fm
  (^{,,,} [x]   (inc x))
  (^{,,,} [x y] (inc (+ x y)))

You could alternatively provide it once:

(fm 
  ^{:fm/args ([int?]
              [int? int?])}
  ([x]   (inc x))
  ([x y] (inc (+ x y)))

Intuitively, either should work and be relatively straightforward (albeit requiring some structural tweaks). Not a super high priority, but something similar to #2 in that it could open up additional applicability for fm, and break fewer fn expectations.

Feature Idea: Anomaly Explanations

It'd be a really useful tool to be able to ask an anomaly to describe itself (similar to s/explain). This would be valuable not just at the REPL but also when building developer tooling. Consider the following:

(require [fm.macros :refer [defm])
(require [fm.anomaly :as anom])

(defm inc_
  ^{:fm/args int?}
  [n]
  (inc n))

(anom/explain (inc 'a))  ;; => "`inc` encountered an `args` anomaly for argument `a` at `0`: `a`  failed predicate `int?`

What's even cooler is that you could potentially leverage additional :fm/* keys for providing a descriptor that anom/explain could utilize when defining predicates to be used by other specs. If that predicate is the one that failed in the spec, anom/explain could utilize the provided :fm/explain fn to generate a more specific explanation. As a bonus we can pass the entire anomaly to the explainer so they can fully leverage the given anomaly data.

;; Pred to be used in some other spec
(defm success?
  ^{:fm/explain
     (fn [anomaly]
       (str "Successful exit codes are zero. Got " (first (:fm.anomaly/args anomaly)) " instead"))}
  [exit-code]
  (zero? exit-code))

(anom/explain (success? 42))  ;; => "`success?` encountered an `args` anomaly for argument `42` at `0`: 42 isn't zero

Thoughts?

Cljs support

resolve is a macro in clojurescript, rather than a regular fn like in clojure, which keeps certain predicates from compiling correctly. there are also slight incompatibilities in other common interop pitfalls such as exception handling.

Variadic signatures and fm/wrap

Beyond supporting all function signatures, variadic specification would be useful in wrapping existing functions unchanged. In addition to being useful on it's own, e.g. in library consumption, a concise way to wrap existing functions would improve anomaly propagation through ->, ->>, comp, etc..

(defm merge2
  ^{:fm/args [map? map? & map?]
    :fm/ret  map?}
  [m1 m2 & ms]
  (apply merge m1 m2 ms))

  ;; `fm/wrap` interface
(fm/wrap <fmeta> <f>)

(fm/wrap
  ^{:fm/args [& map?]
    :fm/ret  map?
  merge)

To support variadic signatures, fm.utils/fm-form should (partition-by #{'&} args-form) and reduce first and last using a modified fm.utils/spec-form. The fm.utils/wrap facility will precipitate out of that improvement.

It seems there are three variadic cases to handle:

  ;; variadic `args-form` cases
[& xs]     ; case 1: symbolic variadic args, xs will be a seq
[& {:k v}] ; case 2: keyword variadic args, xs will be a seq `interleaved` with keywords
[& [x1]]   ; case 3: positional variadic args

For case 1, :fm/args should accept a single spec to be applied to every variadic argument (e.g. [& map?] above), which the updated fm.utils/spec-form would wrap in s/*, inferring every variadic argument should conform to that spec.

:fm/args should also accept a sequence spec form, such as s/+, s/alt, s/? etc., to apply directly.

It isn't clear if inferring s/alt would be useful or just increase ambiguity, but technically could work.

  ;; case 1 variadic `:fm/args`
[& map?]                                ; infer `(s/* map?)`
[& ::spec1]                             ; infer `(s/* ::spec1)`
[& (s/alt :tag1 ::spec1 :tag2 ::spec2)] ; use form directly

  ;; may not be worthwhile
[& ::spec1 ::spec2]                     ; infer `(s/alt ::spec1 ::spec1 ::spec2 ::spec2)`

case 2 needs more thought, but I'm thinking it will boil down to using s/cat.

[& {:k v}]       ; `args-form`
[& {:k string?}] ; `:fm/args` form

case 3 may be as simple as reducing both partitions using the existing facilities.

[& [x]]    ; `args-form`
[& [map?]] ; `:fm/args` form

Global configuration, defaults

in general these should be used carefully, and i'm not totally sold on it yet due to the potential risks, but being able to "flip on" e.g. :fm/trace globally would be extremely useful:

(require '[fm.,,, :as fm.,,,])

(defn log-trace! [t] (,,, t))

(set! *fm.,,,/trace* log-trace!)
,,,

:fm/handler (e.g. for #22) and :fm/conform may also be useful (or at least match the use-case).

one way to potentially reduce accidental mutation is to make fm.,,, something likefm.global or fm.opts or something that needs to be explicitly require'd in order to mutate these bindings, that isnt part of e.g. fm.macro, or another ns commonly require'd in typical fm usage.

reference on dynamic vars.

fm/branch

(->>
  is-it-pronounced-data-or-data
  (map xf1)
  ,,,
  (fm/branch ::spec1
    (true-expr ,,,)
    (false-expr ,,,)))

fm->>

(fm->>
  ^{:fm/args ,,,}
  (map xf1 data1)
  ,,,
  ^{:fm/ret ,,,}
  (into {}))

etc., etc..

sincerely,
@localshred

Support docstring with defm

Currently defm doesn't support supplying a docstring for a defm'd function:

(defm inc
  "I should be able to add a docstring"
  ^{:fm/args number?
     :fm/ret number?}
  [n]
  (inc n))

This currently fails with:

Syntax error macroexpanding clojure.core/fn at (*cider-repl localshred/fm:localhost:62824(clj)*:272:8).
(...[charlist of the docstring]...) - failed: Extra input at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/\
param-list
\T - failed: number? at: [:fn-tail :arity-n :params] spec: :clojure.core.specs.alpha/param-list```

Which basically just means that it's expecting the first thing passed to `defm` to be the meta piece. I'm guessing this is trivial to support but definitely not a high priority.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.