All Projects → Invocatis → Motif

Invocatis / Motif

Licence: gpl-3.0
Recursive, data driven pattern matching for Clojure

Programming Languages

clojure
4091 projects

Projects that are alternatives of or similar to Motif

chemin
🥾 A type-safe pattern builder & route matching library written in TypeScript
Stars: ✭ 37 (-41.27%)
Mutual labels:  matching, pattern
Pattern Matching Ts
⚡ Pattern Matching in Typescript
Stars: ✭ 107 (+69.84%)
Mutual labels:  matching, pattern
dir-glob
Convert directories to glob compatible strings
Stars: ✭ 41 (-34.92%)
Mutual labels:  matching, pattern
Ts Pattern
🎨 A complete Pattern Matching library for TypeScript, with smart type inference.
Stars: ✭ 854 (+1255.56%)
Mutual labels:  matching, pattern
allot
Parse placeholder and wildcard text commands
Stars: ✭ 51 (-19.05%)
Mutual labels:  matching, pattern
Example Render Callback
An example of sharing stateful logic across React components using the Render Callback (aka Function as Child) pattern
Stars: ✭ 17 (-73.02%)
Mutual labels:  pattern
Textblob
Simple, Pythonic, text processing--Sentiment analysis, part-of-speech tagging, noun phrase extraction, translation, and more.
Stars: ✭ 7,991 (+12584.13%)
Mutual labels:  pattern
Gms Feature Matcher
GMS: Grid-based Motion Statistics for Fast, Ultra-robust Feature Correspondence (CVPR 17 & IJCV 20)
Stars: ✭ 797 (+1165.08%)
Mutual labels:  matching
Commonregex
🍫 A collection of common regular expressions for Go
Stars: ✭ 733 (+1063.49%)
Mutual labels:  pattern
Fast Topic Matching
Messaging middleware topic matching implementations.
Stars: ✭ 52 (-17.46%)
Mutual labels:  matching
Deepmatch
A deep matching model library for recommendations & advertising. It's easy to train models and to export representation vectors which can be used for ANN search.
Stars: ✭ 1,051 (+1568.25%)
Mutual labels:  matching
Extending Tea
Example of an extension to The Elm Architecture
Stars: ✭ 30 (-52.38%)
Mutual labels:  pattern
Pytest Patterns
A couple of examples showing how pytest and its plugins can be combined to solve real-world needs.
Stars: ✭ 24 (-61.9%)
Mutual labels:  pattern
Naming Cheatsheet
Comprehensive language-agnostic guidelines on variables naming. Home of the A/HC/LC pattern.
Stars: ✭ 9,475 (+14939.68%)
Mutual labels:  pattern
Wire Render Pattern
☄️ Wire Render Pattern for Processwire
Stars: ✭ 16 (-74.6%)
Mutual labels:  pattern
Input Mask Android
User input masking library repo.
Stars: ✭ 1,060 (+1582.54%)
Mutual labels:  pattern
Guardclauses
A simple package with guard clause extensions.
Stars: ✭ 767 (+1117.46%)
Mutual labels:  pattern
Patterns Cheatsheet
Software design patterns cheatsheet.
Stars: ✭ 47 (-25.4%)
Mutual labels:  pattern
Test match
Recursive matching
Stars: ✭ 20 (-68.25%)
Mutual labels:  matching
Flutter realworld example app
Work in progress...
Stars: ✭ 12 (-80.95%)
Mutual labels:  pattern

motif

Recursive, data driven, pattern matching for Clojure

Releases and dependency information

CircleCI

[motif "1.0.1"]
<dependency>
  <groupId>motif</groupId>
  <artifactId>motif</artifactId>
  <version>1.0.1</version>
</dependency>

Overview

Motif brings expressive pattern matching to Clojure. Motif focuses on using the power of our core Clojure data structures, giving each its own meaning and possibilities.

Let's get started!

(require '[motif.core :refer [matches?] :as motif])

Patterns

Value Patterns

Simple value patterns are simply compared via equality.

(matches? 1 1) ;=> true

(matches? 1 2) ;=> false

Function patterns

Functions as patterns are invoked on the given target. Monadic predicates are only supported.

(matches? pos? 1) ;=> true

(matches? string? 1) ;=> false

(matches? inc 1) ;=> true

(matches? (fn [x y]) 1) ;=> false, due to ArityException

Regex patterns

Regex patterns are compared to the string representations of their targets as with clojure.core/re-matches.

(matches? #"\d*" "123") ;=> true

(matches? #"\d*" 123) ;=> true

(matches? #"\[(\d*\s)*\d*\]" [1 2 3]) ;=> true

Vector patterns

Vectors compare each element ordinally, that is the first element of the pattern is compared to the first of the target, the second to the second, and so on. Vectors require their targets to be at least as long as their patterns, but the targets can be longer than the patterns.

(matches? [1 2] [1 2]) ;=> true

(matches? [1 2] [1]) ;=> false

(matches? [1 2] [1 2 3]) ;=> true

(matches? [pos? neg?] [1 -1]) ;=> true

(matches? [\a \b] "ab") ;=> true

Seq patterns

Seq patterns work similarly to vector patterns, however, they can be longer or shorter than their targets. This allows for infinite length sequences to be used as patterns, however, beware of infinite loops!

(matches? '(1 2 3) [1]) ;=> true

(matches? '(1) [1 2 3 4]) ;=> true

(matches? (repeat even?) [2 2 2 2]) ;=> true

(matches? (repeat odd?) (repeat 1)) ;=> infinite loop!

Map patterns

Maps compare corresponding key values with matches? Maps a conjunctive, and thus all keys much match positively. Extra keys in the target map are acceptable, and ignored by the pattern. Keys in the pattern are not required to be in the target map.

(matches? {:key :pattern-value} {:key :target-value}) ;== (matches? :pattern-value :target-value)

(matches? {1 2} {1 2 3 4}) ;=> true

(matches? {1 2 3 4} {1 2}) ;=> false

(matches? {:key1 :value :key2 nil} {:key1 :value}) ;=> true

(matches? {:key1 :value :key2 nil} {:key1 :value :key2 nil}) ;=> true

If a key in the pattern is an function (ifn?), it is instead applied to the target as apposed to gotten with get; this opens up another dimension of expressiveness using maps.

(matches? {:key 1 #(count (keys %)) 1} {:key 1}) ;=> true

(matches? {pos? false neg? false} 0) ;=> true

Set patterns

Sets are our disjunctive patterns; they need only one of their elements match against the target.

(matches? #{1 2 3} 1) ;=> true

(matches? #{even? odd?} 0) ;=> true

(matches? #{pos? neg?} 0) ;=> false

Modifiers

Modifiers extend, specify, and hack how the patterns work. Modifiers are attached to their patterns as metadata values.

Equality Modifier

The Equality modifier directs motif to compare values using the = function, rather than matches?. We can use this when motif's special interpretations might get in the way.

(matches? {identity 1} {identity 1}) ;=> false

(matches? ^:= {identity 1} {identity 1}) ;=> true

Use Modifier

The Use modifier explicitly directs motif to use a different function than matches?. The Equality modifier is a special case of the use modifier.

(defn same-keys?
  [m0 m1]
  (= (set (keys m0))
     (set (keys m1))))

(matches? {:x 1 :y 2} {:x 3 :y 4}) ;=> false

(matches? ^{:use same-keys?} {:x 1 :y 2} {:x 3 :y 4}) ;=> true

Getter Modifier

Exclusive to maps, this modifier explicitly defines how keys are used to access their values in maps. In cases where you have a map with function keys, this may be useful to stop them from being applied to the map and instead be gotten with get.

(matches? ^{:getter get} {pos? neg? neg? pos?} {pos? -2 neg? 2}) ;=> true

Star Modifier

The Star modifier maps the pattern over the target, requiring all elements to match. More specifically, the target is seq'd, and each element is matched against the pattern. If every element matches, the pattern matches.

(matches? #{1 2} [1 2]) ;=> false

(matches? ^:* #{1 2} [1 2]) ;=> true

Additionally, a positive integral value can be passed to the star modifier. The number will define how many extra times motif should seq targets before matching against the pattern. The default value is 0, and thus only does one seq.

(matches? ^{:* 1} (fn [n] (integer? n)) [[1 2] [3 4] [5 6]]) ;=> true

Meta Modifier

Since modifiers take up the information space of the structures metadata, the Meta Modifier adds another area for metadata to be matched. That is, the metadata of the target is matched against the values in the Meta Modifier. The values are matched in the same way as in matches?, so all patterns discussed work within the modifier.

(matches? {:y 1} ^{:x 1} {:y 1}) ;=> true

(matches? ^{:meta {:x 2}} {:y 1} ^{:x 1} {:y 1}) ;=> false

(matches? ^{:meta {:x ^:* #{1 2}}} {:y 1} ^{:x [1 2]} {:y 1}) ;=> true

Strict Modifier

The Strict modifier is interpreted different for each pattern type, though, it in general reduces some of the laxness that our patterns might have.

Strict Maps

Strict maps require equality in keys.

(matches? {:x 1} {:x 1 :y 2}) ;=> true

(matches? ^:! {:x 1} {:x 1 :y 2}) ;=> false

(matches? ^:! {:x 1 :y 2} {:x 1 :y 2}) ;=> true

Strict Sets

Sets require at least one element to match the target. Strict sets strengthen this requirement to require one and only one element to match the target.

(matches? #{1 pos?} 1) ;=> true

(matches? ^:! #{1 pos?} 1) ;=> false

Strict Vectors

Strict Vectors require the length of their targets be the same as their own length.

(matches? [1 2 3] [1 2 3 4]) ;=> true

(matches? ^:! [1 2 3] [1 2 3 4]) ;=> false

Logical Implementations

It is worth noting the implicit ability to create logical patterns using our given tools. Maps require all pattern elements to match, a natural implementation of and. Sets require only one pattern to match, being an implementation of or. Finally, strict sets require one and only one element match the pattern, a perfect representation of xor. Here's a few examples:

; Or
(matches? #{integer? pos? odd?} -2) ;=> true

; And
(matches? {integer? true pos? true even? true} 2) ;=> true

; Xor
(matches? ^:! #{pos? neg?} 1) ;=> true
(matches? ^:! #{pos? neg?} 0) ;=> false
(matches? ^:1 #{pos? odd?} 1) ;=> false

Here we find ourselves with a slight discomfort: It can increase tedium and decrease legibility to write true as a value for simple and maps. Similarly, or as sets has less robustness since only boolean values can be compared. To fix this, we have additional modifiers ^:& and ^:| to imply conjunctive/disjunctive natures, respectively.

; or map
(matches? ^:| {:x 1 :y 2} {:x 1 :y 3}) ;=> true

; and set
(matches? ^:& #{pos? even?} -2) ;=> false

Some things to note

Non-symmetry

matches? is not symmetric! That is (matches? a b) does not imply (matches? b a). This is due to patterns being treated differently that their targets. Consider the following:

(matches {:x 1 nil? true} {:x 1}) ;=> false

(matches {:x 1} {:x 1 nil? true}) ;=> true

This asymmetry is due to how various structures are interpreted when they are used as patterns.

Exceptions are failures

Any exceptions thrown by patterns are treated as general failures, causing motif to return false.

(matches? #(throw (Exception. "Some Exception")) nil) ;=> false

However, be aware that exceptions will be thrown during the pattern compilation step; so if there's something wrong with your pattern, it will be represented as an exception.

Ordering of Patterns

In most instances, clojure implements sets and maps using hashing, which does not guarantee ordering. There may arise some cases where you want one element of a pattern to be executed before another. In these cases one should remember the use of array-map and, though not included in core, ordered-set.

Modifier Tag Precedence

As some modifiers completely change the course of interpretation, there is an implicit precedence in which some tags nullify others.

The ^:use and ^:= tags void all other tags, as the matches? semantics are disregarded.

Strictness in sets ^:! supersedes conjunction ^:&.

Strictness in maps ^:! does not interfere with disjunction ^:|, though will result in strange effects. It is likely these effects are not desired, so avoid using both.

Meta ^:meta and star ^:* tags play nicely with others.

Meta Info and Compiler Nuances

Due to how the compiler has been implemented, meta macros are not picked up when applied to symbols referring to vars ((meta ^:x meta) ;=> nil). When modifiers are needed on variable values, remember the with-meta function ((meta (with-meta meta {:x true})) ;=> {:x true}. This also applies to function application results ((with-meta (identity inc) {:x 1})).

Function literals, however, pick up meta macros just fine. Consider wrapping your function in a literal: (matches? ^:* (fn [x] (integer? x)) [[1 2 3] [4 5 6] [7 8 9]]).

Any Function

We've added a convenience function to the library. _ is the same as clojure.core/any?, but has a more idiomatic feel to it.

(require '[motif.core :refer [_ matches?]])
(matches? _ 1) ;=> true
(matches? _ nil) ;=> true
(matches? {:x _} {:x {:y 1}}) ;=> true

Match Macro

Included in motif.core is thematch macro. match works the same as (condp = x ...).

(match 1
  neg? "negative"
  zero? "zero"
  pos? "positive") ;=> positive

That's it!

That's all you need to go out into the world. But before you go, let's look at some fun examples that might help illuminate some of the possibilities:

(matches? (interleave (repeat odd?) (repeat even?)) [1 2 3 4]) ;=> true

(matches? {first 1 last 4} [1 2 3 4]) ;=> true

(matches? {0 1 1 2 2 3 3 4} [1 2 3 4]) ;=> true

(matches? {some? true inc 1} 0) ;=> true

(matches? {(partial reduce max) 4} [1 2 3 4]) ;=> true

(matches? (complement #{1 2 3}) 4) ;=> true

(matches? {:x nil keys ^:* #{:x}} {:x nil}) ;=> true

(matches? {:x nil keys ^:* #{:x}} {:x nil :y nil}) ;=> false

(matches? ^:* ^:! {:x pos?} [{:x 1} {:x 2} {:x 3} {:x 4}]) ;=> true

(matches? ^{:meta {(comp set keys) ^:= #{:x :y}}} {:a pos?} ^{:x 1 :y 2} {:a 1}) ;=> true

(matches? ^{:use clojure.set/subset?} #{1 2 3} #{1 2 3 4}) ;=> true

(matches? ^{:getter (fn [target key] (inc key))} {0 1 1 2 2 3} {})
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].