This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.

ModularDesign.Operators

Infix operators for use with the Modular Design framework

As I have been developing the Modular Design package, I have also been evolving a personal style guide for Elm programming (not yet released). My style guide conforms to the official Elm style guide, but makes some very opinionated choices so that code blocks are always formatted in a consistent way. The main principle motivating these choices is the idea that a programmer should be able to readily discern the sequence of function calls in a code block by visually scanning the code from top to bottom, and then from left to right.

According to this principle, the "data" — that is, the value or set of values that the function is called on — should always appear at the top of the code block, with functions called on the data appearing on subsequent lines. A nested series of function calls can appear on one line, but more complex code chunks should be broken up into self-contained functions using let..in statements. Nested list brackets and complex code chunks within list brackets should generally be avoided.

With the above principle, the |> operator is used very liberally, the >> operator is used only in rare cases, and there is no use case for "reverse" (right-to-left) functional operators. The modular approch to HTML and CSS I have implemented in the Modular Design package allows this style to be maintained when coding view components. The evolving style guide, in turn, has been part of the motivation behind various semantic/syntactical choices in my implementation of the package.

In addition to liberal use of the |> operator, I have found use cases for a small set of custom operators that help to maintain consistent visual formatting and enhance readability of code blocks. While I recognize it is advisable to keep custom operators to a minimum so that code does not become needlessly obscure, I intend the operators included in this module to become a standard part of the Modular Design framework, so users of this framework should learn to recognize them and apply them where appropriate.

Appending Things

Function Application with Lists

Error Handling with Maybe and Result Values

Uncurry Operators

module ModularDesign.Operators exposing
  ( (|++), (|::), (:+:)
  , (.|>), (:|>)
  , (?=), (!=), (?|>), (!|>)
  , (@@|>), (@@@|>)
  )

{-|

## Infix operators for use with the Modular Design framework

As I have been developing the Modular Design package, I have also been
evolving a personal style guide for Elm programming (not yet released). My
style guide conforms to the
[official Elm style guide](http://elm-lang.org/docs/style-guide),
but makes some very opinionated choices so that code blocks are always formatted
in a consistent way. The main principle motivating these choices is the idea
that a programmer should be able to readily discern the sequence of function
calls in a code block by visually scanning the code from top to bottom, and then
from left to right.

According to this principle, the "data" — that is, the value or set of values
that the function is called *on* — should always appear at the top of the code
block, with functions called on the data appearing on subsequent lines. A
nested series of function calls can appear on one line, but more complex code
chunks should be broken up into self-contained functions using `let..in`
statements. Nested list brackets and complex code chunks within list brackets
should generally be avoided.

With the above principle, the `|>` operator is used very liberally, the `>>`
operator is used only in rare cases, and there is no use case for "reverse"
(right-to-left) functional operators. The modular approch to HTML and CSS
I have implemented in the Modular Design package allows this style to be
maintained when coding view components. The evolving style guide, in turn, has
been part of the motivation behind various semantic/syntactical choices in my
implementation of the package.

In addition to liberal use of the `|>` operator, I have found use cases for a
small set of custom operators that help to maintain consistent visual formatting
and enhance readability of code blocks. While I recognize it is advisable to
keep custom operators to a minimum so that code does not become needlessly
obscure, I intend the operators included in this module to become a standard
part of the Modular Design framework, so users of this framework should learn
to recognize them and apply them where appropriate.

# Appending Things
@docs (|++), (|::), (:+:)

# Function Application with Lists
@docs (.|>), (:|>)

# Error Handling with `Maybe` and `Result` Values
@docs (?=), (!=), (?|>), (!|>)

# Uncurry Operators
@docs (@@|>), (@@@|>)
-}

import List
import Maybe
import Result


--APPENDING THINGS

{-| Append the RHS to the end of the LHS; equivalent to `++`, but
left-associative with precedence set to `0` (same as `|>`)

    ("ba" |> String.reverse) ++ "c"       --> "abc"
    "ba" |> String.reverse ++ "c"         --> ERROR
    "ba" |> String.reverse |++ "c"        --> "abc"

-}
(|++) : appendable -> appendable -> appendable
(|++) a b =
  a ++ b

infixl 0 |++


{-| Append the item on the RHS to the end of the list on the LHS

    [1] |:: 2         --> [1,2]
    [1] |:: 2 |:: 3   --> [1,2,3]
-}
(|::) : List a -> a -> List a
(|::) list a =
  list ++ [ a ]

infixl 0 |::


{-| Construct a list from the LHS and RHS; precedence is 5 and associativity is
right (same as `++`)

    1 :+: 2   --> [1, 2]
-}
(:+:) : a -> a -> List a
(:+:) a1 a2 =
  [ a1, a2 ]

infixr 5 :+:


--FUNCTION APPLICATION WITH LISTS

{-| Forward operator for List.map

    [1,4,9] .|> sqrt    --> [1,2,3]
-}
(.|>) : List a -> (a -> b) -> List b
(.|>) list f =
  List.map f list

infixl 0 .|>


{-| Wrap LHS in a list, then apply RHS function

    1 :|> List.head   --> Just 1
-}
(:|>) : a -> (List a -> b) -> b
(:|>) a f =
  f [ a ]

infixl 0 :|>


-- ERROR HANDLING

{-| Forward operator for Maybe.withDefault

    Just 42 ?= 100    --> 42
    Nothing ?= 100    --> 100
-}
(?=) : Maybe a -> a -> a
(?=) maybeValue defaultValue =
  Maybe.withDefault defaultValue maybeValue

infixl 0 ?=


{-| Forward operator for Result.withDefault

    String.toInt "123" != 0   --> 123
    String.toInt "abc" != 0   --> 0
-}
(!=) : Result x a -> a -> a
(!=) resultValue defaultValue =
  Result.withDefault defaultValue resultValue

infixl 0 !=


{-| Forward operator for Maybe.map

    Just 9 ?|> sqrt     --> Just 3
    Nothing ?|> sqrt    --> Nothing

-}
(?|>) : Maybe a -> (a -> b) -> Maybe b
(?|>) maybeValue f =
  Maybe.map f maybeValue

infixl 0 ?|>


{-| Forward operator for Result.map

    Ok 4.0 !|> sqrt             --> Ok 2.0
    Err "bad input" !|> sqrt    --> Err "bad input"

-}
(!|>) : Result x a -> (a -> value) -> Result x value
(!|>) resultValue f =
  Result.map f resultValue

infixl 0 !|>


--UNCURRYING

{-| Forward operator for `uncurry` with 2 parameters

    (1,2) @@|> (+)    --> 3
-}
(@@|>) : (a, b) -> (a -> b -> c) -> c
(@@|>) params f =
  uncurry f params

infixl 0 @@|>


{-| Forward operator for `uncurry` with 3 parameters

    (10, 20, 30) @@@|> clamp    --> 20
-}
(@@@|>) : (a, b, c) -> (a -> b -> c -> d) -> d
(@@@|>) (a, b, c) f =
  f a b c

infixl 0 @@@|>