Skip to content

Misshapen structures

Brian Marick edited this page Mar 20, 2016 · 3 revisions

If a structure is so misshapen that, for example, built-like tries to follow the path [:a :b] into {:a 1}, an error results. These errors are handled the same as errors from failing checkers. For example:

user=> (built-like {[ALL :a] even?} 
                   [{:a 1} {:a 2} :not-a-map])
[0 :a] should be `even?`; it is `1`
[2 :a] encountered `:not-a-map` when a map or record was expected
=> nil

The rest of this page describes what kinds of values count as "misshapen". What happens if a path is too long for a structure - such as following [:a :b] into {} - is described in truncated structures. nil presents issues that deserve their own page.

  • Keywords and strings require maps or records

    Whereas Clojure is happy to extract the :a field from an integer:

    user=> (get 1 :a)
    => nil

    ... this library is less forgiving:

    user=> (built-like {:a even?} 1)
    :a encountered `1` when a map or record was expected
    nil
  • ALL requires non-map collections

    user=> (built-like {[ALL] string?} 1)
    [ALL] encountered `1` when a collection was expected
    => nil
    
    user=> (built-like {[ALL] string?} {:a 1})
    [ALL] encountered map or record `{:a 1}`; ALL doesn't allow that
    => nil

    Maps are rejected because I think that an attempt to descend into each of the key-value pairs making up the map is awfully likely to indicate a map is where it didn't belong. Far more likely, in fact, than that descending into a map as a collection of pairs provides some useful checking that couldn't be done more clearly otherwise. But I could be convinced to change my mind.

  • RANGE and integers require sequential collections

    user=> (built-like {[(RANGE 1 2)] even?} 1)
    [(RANGE 1 2)] encountered `1` when a sequential collection was expected
    => nil
    
    user=> (built-like {[1] even?} #{1 2 3})
    [1] encountered `#{1 3 2}` when a sequential collection was expected
    => nil

    Given that collections like sets and maps are unordered, selecting a range of indexes makes no sense.

  • Predicates that blow up count as falsey.

    Predicates prevent further descent unless they have a truthy value. An exception is not a truthy value, so it prevents further descent. There's no indication of the exception in the output.

    user=> (built-like {[ALL even?] pos?} [-1 :boom 3])
    [-1 :boom 3]

    In this example, all even values (of which there are none) are in fact positive.