> This is very cool.  You might take a look at the `threading` module for
> additional ideas: https://docs.racket-lang.org/threading/index.html

Yeh, the threading macro is what took me down this rabbit-hole. But I wanted an 
infix operator, mostly because of my pernicious unix habit. I found there are 
some other advantages too. You need fewer parens, can mix thread-first and 
thread-last, and it makes reading nested operations easier. I don't think I'll 
go back to prefix :)

I even experiment with making the whole language infix... as in (data proc 
params). It was interesting, but ultimately I concluded that prefix should be 
the default, and infix should be the special case.

> 
> On Tue, Mar 9, 2021 at 10:20 AM Roger Keays <[email protected]> wrote:
> 
>     Hi all,
> 
>     I recently publish a new package called *fluent* which adds some syntax
>     enhancements to Racket. Links and README below. Let me know what you
>     think...
> 
>     Roger
> 
>     https://pkgs.racket-lang.org/package/fluent
>     https://github.com/rogerkeays/racket-fluent/
> 
>     # fluent
> 
>     UNIX style pipes and a lambda shorthand syntax to make your Racket code
>     more readable.
> 
>     ## ? Unpopular So LISP Is Why
> 
>     Let's be honest. LISP missed a huge opportunity to change the world by
>     telling developers they have to think backwards. Meanwhile, UNIX became
>     successful largely because it allows you to compose programs sequentially
>     using pipes. Compare the difference (the LISP example is actually racket):
> 
>         UNIX: cat data.txt | grep "active" | cut -f 4 | uniq | sort
>         LISP: (sort (remove-duplicates (map (λ (line) (list-ref (string-split
>     line) 4)) ((filter (λ (line) (string-contains? line "active")) 
> (file->lines
>     "data.txt"))))))
> 
>     Using *fluent*, the same racket code can be written according to the UNIX
>     philosophy:
> 
>         ("data.txt" > file->lines >> filter (line : line > string-contains?
>     "active") >> map (line : line > string-split > list-ref 4) >
>     remove-duplicates > sort)
> 
>     You can use unicode → instead of > if you prefer. It is more distinctive
>     and a bit easier on the eyes:
> 
>         ("data.txt" → file->lines →→ filter (line : line → string-contains?
>     "active") →→ map (line : line → string-split → list-ref 4) →
>     remove-duplicates → sort)
> 
>     ## Function Composition
> 
>     Using the function composition operator (> or →), *fluent* inserts the 
> left
>     hand side as the first parameter to the procedure on the right hand side.
>     Use >> (or →→) to add the left hand side as the last parameter to the
>     procedure.
> 
>         (data > procedure params)     becomes    (procedure data params)
>         (data >> procedure params)    becomes    (procedure params data)
> 
>     This operation can be chained or nested as demonstrated in the examples.
> 
>     ## Lambda Shorthand
> 
>     The : operator allows you to easily write a lambda function with one
>     expression. Parameters go on the left, the expression on the right, no
>     parentheses required. For example:
> 
>         > ((x : + x 1) 1)
>         2
>         > ((x y : + x y) 1 2)
>         3
>         > (map (x : string-upcase x) '("a" "b" "c"))
>         '("A" "B" "C")
> 
>     ## Math Procedures
> 
>     Since this library uses > for function composition, the built in
>     greater-than procedure is renamed to `gt?`. Note, this could break 
> existing
>     code if you are already using the > procedure. Other math procedures are
>     also renamed for consistency, and because the text versions read more
>     naturally when using function composition.
> 
>         > gt?
>         < lt?
>         >= gte?
>         <= lte?
>         + add
>         - subtract
>         * multiply
>         / divide
> 
>     ## Convenience Procedures
> 
>     *fluent* works best when the data (input) parameter comes first. Most
>     racket functions do this out of the box, but many functions which take a
>     procedure as a parameter put the data last. That's fine, because you can
>     just use >>. Alternatively you can wrap and rename the procedure, which is
>     what we've done for these functions:
> 
>         original   data-first version
>         -----------------------------
>         for-each   iterate
> 
>     example:
> 
>         > ('(1 2 3) → iterate (x : displayln x))
>         1
>         2
>         3
> 
>     ## Comparison to Clojure's Threading Macro
> 
>     Clojure's threading macro is a prefix operator, which means it is less
>     readable when nested and requires more parentheses. You could say that the
>     *fluent* infix operator acts as one parenthesis. Compare:
> 
>     CLOJURE (prefix):
> 
>         (-> (list (-> (-> id3 (hash-ref 'genre "unknown")) normalise-field)
>                   (-> (-> id3 (hash-ref 'track "0")) normalise-field)
>                   (-> (-> id3 (hash-ref 'artist "unknown")) normalise-field)
>                   (-> (-> id3 (hash-ref 'title "unknown")) normalise-field))
>     (string-join "."))
> 
>     FLUENT (infix):
> 
>         (list (id3 → hash-ref 'genre "unknown" → normalise-field)
>               (id3 → hash-ref 'track "0" → normalise-field)
>               (id3 → hash-ref 'artist "unknown" → normalise-field)
>               (id3 → hash-ref 'title "unknown" → normalise-field)) →
>     string-join ".")
> 
>     Fluent's infix approach also makes it easier to combine thread-first (→)
>     with thread-last (→→).
> 
>     ## How to enter → with your keyboard
> 
>     → is Unicode character 2192. On linux you can enter this using
>     `shift-ctrl-u 2192 enter`. Naturally, if you want to use this character,
>     you should map it to some unused key on your keyboard. This can be done
>     with xmodmap:
> 
>         # use xev to get the keycode
>         $ xev
> 
>         # check the current mapping
>         $ xmodmap -pke
> 
>         # replace the mapping
>         $ xmodmap -e "keycode 51=U2192 Ccedilla ccedilla Ccedilla braceright
>     breve braceright"
> 
>     Making this change permanent depends on your session manager. Search
>     duckduckgo for details.
> 
>     ## Installation
> 
>     This library is available from the Racket package collection and can be
>     installed with raco:
> 
>         $ raco pkg install fluent
> 
>     All you need to do is `(require fluent)`. You can try it out in the REPL:
> 
>         > (require fluent)
>         > ("FOO" > string-downcase)
>         "foo"
>         > ((x y : x > add y) 1 2)
>         3
> 
>     --
>     You received this message because you are subscribed to the Google Groups
>     "Racket Users" group.
>     To unsubscribe from this group and stop receiving emails from it, send an
>     email to [email protected].
>     To view this discussion on the web visit 
> https://groups.google.com/d/msgid/
>     racket-users/sigid.1702887e81.20210309152029.GA3105%40papaya.papaya.
> 

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/sigid.1703b50dc5.20210310133652.GB3081%40papaya.papaya.

Reply via email to