• Stars
    star
    557
  • Rank 79,968 (Top 2 %)
  • Language
    Clojure
  • Created over 12 years ago
  • Updated over 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

a collection of arrow macros

swiss-arrows

A collection of arrow macros.

trystero

Usage

Installation

http://clojars.org/swiss-arrows

Getting Started

(ns example.core
  (:require [swiss.arrows :refer :all]))

Overview

-<> , -<>> The Diamond Wand, Diamond Spear

some-<> , some-<>> The Nil-shortcutting Diamond Wand

apply-> , apply->> Applicative arrows (WIP)

-!> , -!>> , -!<> Non-updating Arrows

<<- The Back Arrow

-< , -<:p The Furcula, Parallel Furcula

-<< , -<<:p The Trystero Furcula, Parallel Trystero Furcula

-<>< , -<><:p The Diamond Fishing Rod, Parallel Diamond Fishing Rod

A Generalization of the Arrow

The Diamond Wand - similar to -> or ->> except that the flow of execution is passed through specified <> positions in each of the forms.

(-<> 2
     (* <> 5)
     (vector 1 2 <> 3 4))
 => [1 2 10 3 4]

The diamond wand also supports literals:

 ;; map
 (-<> 'foo {:a <> :b 'bar}) => {:a 'foo :b 'bar}

 ;; vector
 (-<> 10 [1 2 3 <> 4 5])    => [1 2 3 10 4 5]

Like -> & ->> interpret a symbol x as (x), -<> interprets x as (x <>)

 (-<> :a
      (map <> [{:a 1} {:a 2}])
      (map (partial + 2) <>)
      reverse)
 => [4 3]

Default Positioning Behavior

If no <> position marker is found in a form within the Diamond Wand -<>, the default positioning behavior follows that of the -> macro. Likewise, if no position is specified in a form within the Diamond Spear -<>>, the default is has the positioning semantics of ->>.

Some examples:

  (-<> 0 [1 2 3]) => [0 1 2 3]

  (-<> 0 (list 1 2 3)) => '(0 1 2 3)

  (-<>> 4 (conj [1 2 3])) => [1 2 3 4]

  (-<> 4 (cons [1 2 3]) reverse (map inc <>)) => [4 3 2 5])

Nil-shortcutting Diamond Wand

 (some-<> "abc"
   (if (string? "adf") nil <>)
   (str <> " + more"))
 => nil

Applicative Arrows (WIP)

 (apply->>
   [[1 2] [3 4]]
   (concat [5 6]))        => [5 6 1 2 3 4]

 (apply->> [[1 2] [3 4]]
   (concat [5 6])
   +)                     => 21

Based on idea suggested by @rebcabin:

β€œSince I noticed that the "nil-shortcutting diamond wand" acts like the Maybe monad, I started getting the feeling that the swiss arrows could be generalized over all monads. Since the archetype of monads is the sequence monad and the mother operator, bind, for sequence is (apply concat (map my-foo your-sequence-monad)) I started to see chaining of apply-concat as a start toward monadic swiss arrows :)

Also, Wolfram / Mathematica have a host of operators that thread and merge Apply around expressions (see http://reference.wolfram.com/mathematica/ref/Apply.html). Mathematica was designed before monads were formalized in programming languages, but their precursors are all over Mathematica, for instance in the frequent use of Apply. [edit: I should add, contextually, that I am a big admirer of Mathematica just as a programming language, never mind its huge knowledge base of math. I often refer to it for ideas to bring to Clojure and other languages.]

As for the first argument being special, that didn't seem out-of-step with the other arrows, none of which, for instance, can take an expression with an angle-hole "<>" in the first position, and all of which take expressions-with-angle-holes in every slot except the first, modulo the defaults. The defaults are abundant and require the same kind of mental substitution that apply->> etc. would require, so the overall design has established the precedent of "implicits."”

Non-updating Arrows (for unobtrusive side-effecting)

It is often expedient, in particular for debugging and logging, to stick a side-effecting form midway in the pipeline of an arrow. One solution is a pair of utility macros "with" and "within" A caveat of that approach is that having too many anaphoric macros can lead to messy code, and they don't nest (eg. #( ) reader macro), and so on.

Non-updating arrows offer an adequately elegant alternative solution for inserting side-action in what would otherwise be a difficult situation. As a bonus, the arrow-style macros (including the wand-- the <> does not refer to any sort of binding, and does not act recursively so it is not anaphoric in the usual sense, if at all) do not rely on symbol capture, and therefore are arbitrarily nestable.

  (-> {:foo "bar"}
    (assoc :baz ["quux" "you"])
    (-!> :baz second (prn "got here"))
    (-!>> :baz first (prn "got here"))
    (-!<> :baz second (prn "got" <> "here"))
    (assoc :bar "foo"))
  => {:foo "bar"
      :baz ["quux" "you"]
      :bar "foo"}

The Back Arrow

This is simply ->> with its arguments reversed, convenient in some cases. It was suggested as an alternative to egamble/let-else.

 (<<-
  (let [x 'nonsense])
  (if-not x 'foo)
  (let [more 'blah] more)) => 'blah

Branching Arrows

The following six arrows (three, and their parallel counterparts) are branching arrows, in contrast with the "threading" or "nesting" arrows we have seen thus far. The following example demonstrates how branching and nesting arrows can work together to cleanly express a flow of control. Here our first branching arrow, The Furcula, passes the result of (+ 1 2) to each of the successive forms, which is then nested out horizontally into further expressions using traditional arrows.

(-< (+ 1 2)
    (->> vector (repeat 3))
    (-> (* 2) list)
    (list 4))                =>    '[([3] [3] [3]) (6) (3 4)]

The Furcula - a branching arrow using the -> form placement convention. Expands to a let performing the initial operation, and then individual expressions using it. In the parallel version, the individual expressions are evaluated in futures.

(-< (+ 1 2) (list 2) (list 3) (list 4)) => '[(3 2) (3 3) (3 4)]

;; The Parallel Furcula

(-<:p (+ 1 2) (list 2) (list 3) (list 4)) => '[(3 2) (3 3) (3 4)]

The Trystero Furcula - another branching arrow. Same idea as -<, except it uses the ->> form placement convention.

(-<< (+ 1 2) (list 2 1) (list 5 7) (list 9 4)) => '[(2 1 3) (5 7 3) (9 4 3)]

;; Parallel Trystero Furcula

(-<<:p (+ 1 2) (list 2 1) (list 5 7) (list 9 4)) => '[(2 1 3) (5 7 3) (9 4 3)]

The Diamond Fishing Rod - another branching arrow. Same idea as -< and -<<, except it uses the -<> form placement convention.

(-<>< (+ 1 2) [<> 2 1] [5 <> 7] [9 4 <>]) => '[(3 2 1) (5 3 7) (9 4 3)]

;; Parallel Diamond Fishing Rod

(-<><:p (+ 1 2) [<> 2 1] [5 <> 7] [9 4 <>]) => '[(3 2 1) (5 3 7) (9 4 3)]

See the tests for more examples.

License

Credits:

Walter Tetzner, Stephen Compall, and I designed and implemented something similar to the "diamond wand" a couple of years ago.

Thanks to Alex Baranosky, Roman Perepelitsa, Paul Dorman, @rebcabin, and Stephen Compall for code contributions and conceptual contributions.

Copyright (C) 2012 Robert P. Levy

Distributed under the Eclipse Public License, the same as Clojure.

More Repositories

1

clojure-python

Improve seamlessness of Clojure Jython interop
Clojure
60
star
2

ojo

Simple and extensible framework for watching and handling file system events using the Java 7 Watch Service API.
Clojure
40
star
3

milieu

An environment-based configuration library.
Clojure
31
star
4

lambic

Sequence transformation by example
Clojure
26
star
5

clojure-nltk

Python's Natural Language Toolkit for Clojure (interop / partial port).
Python
19
star
6

poevolve

A naive model of human creativity / dadaist song-writing code circa 1999-2000
Shell
7
star
7

sayoperation

A game to collect data for experiments in machine learning of natural language semantics.
Clojure
4
star
8

mostly-useful

Mostly useful utility functions.
Clojure
4
star
9

lein-deploy-app

A Leiningen plugin to push application uberjars to an AWS s3 bucket, organized by application and branch.
Clojure
3
star
10

clojure-nltk-examples

Examples demonstrating use of clojure-nltk library
Clojure
3
star
11

fexjr

not what it claims to be (yet)
Clojure
3
star
12

contrib-repl

nREPL middleware / manual-use lib to add all contrib deps
Clojure
2
star
13

dotfiles

dotfiles
Shell
2
star
14

bamm-bamm

Circular tree visualization inspired by Kai Wetzel's Pebbles.
Clojure
2
star
15

brujeria

simple password pattern generator for brute force guessing
Clojure
2
star
16

ulna

A Facebook API app to announce what you are listening to
Clojure
1
star
17

oinque

Tweet heat map
1
star
18

lein-phonegap

A leiningen plugin for building mobile apps in ClojureScript.
1
star
19

kaprekar

Visualization of Kaprekar Numbers
Clojure
1
star
20

funcycle

funcycle
Clojure
1
star
21

pant

Propositions Are Not Types: Naturalizing Information Content in Computing
1
star
22

political-compass

Cybernautical & Political Compass
1
star
23

ojo.extension

Extensions for ojo file-watching library.
Clojure
1
star