├── .gitignore ├── LICENSE ├── README.md ├── bower.json └── src └── Prelewd.purs /.gitignore: -------------------------------------------------------------------------------- 1 | /bower_components/ 2 | /node_modules/ 3 | /.pulp-cache/ 4 | /output/ 5 | /generated-docs/ 6 | /.psc* 7 | /.purs* 8 | /.psa* 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tom Harding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🤖 Prelewd 🙋 2 | 3 | When newcomers to PureScript see some code for the first time, there's a 90% chance that the first comment will be something like, _"What does `$` do?"_, or _"`f <$> a <*> b`? Wat?"_. This library is for people wondering **exactly that**. 4 | 5 | The standard [`Prelude`](https://pursuit.purescript.org/packages/purescript-prelude/3.1.0/docs/Prelude) is _fine_, but it's a bit... well, **monotone**. Wouldn't it look better if they were a little more... **fabulous**? 🦄 Let's look at some of the common operators with more **panache**. 🎩✨ 6 | 7 | _Below is a table of short explanations. For a more fully-formed write-up (and some explanation as to **why** these symbols were chosen), see the [actual code](https://github.com/i-am-tom/purescript-prelewd/blob/master/src/Prelewd.purs)._ 8 | 9 | ## Operators 10 | 11 | | Emoji | Symbol | Explanation | 12 | | -- | -- | -- | 13 | | 🚂 | `<$>` | Map the function to the left over the [`Functor`](https://pursuit.purescript.org/packages/purescript-prelude/3.1.0/docs/Data.Functor#t:Functor)-wrapped value to the right. | 14 | | 🚋 | `<*>` | Apply the functor-wrapped function on the left to the functor-wrapped value on the right. | 15 | | 👉 | `*>` | Combine two [`Apply`](https://pursuit.purescript.org/packages/purescript-prelude/3.1.0/docs/Control.Apply#t:Apply)'s effects, and return the second. | 16 | | 🎉 | `>>=` | Pass the left-hand "inner value" into the right-hand function. | 17 | | 🔙 | `<<<` | Return a function that takes a value, applies it to the right-hand function, and then applies it to the left-hand function. | 18 | | 🔜 | `>>>` | Same as `compose`, but backwards. | 19 | | 💨 | `$` | Apply the result of the expression to the left to the expression on the right. | 20 | | 🐛 | `~>` | `f ~> g` is a function that turns an `f a` to an `g a` for _any_ `a` value. A [**natural transformation**](https://pursuit.purescript.org/packages/purescript-prelude/3.1.0/docs/Data.NaturalTransformation#t:NaturalTransformation). | 21 | | 🙏 | `<>` | Combine two [`Semigroup` values](https://pursuit.purescript.org/packages/purescript-prelude/3.1.0/docs/Data.Semigroup#t:Semigroup). | 22 | | 🔗 | <|> | Combine two [`Alt`](https://pursuit.purescript.org/packages/purescript-control/3.3.0/docs/Control.Alt#t:Alt) values. A good starting intuition is to think of this as a "chain of **fallbacks**". | 23 | | 💄 | `>$<` | "Pre-process" a value for a [`Contravariant` functor](https://pursuit.purescript.org/packages/purescript-contravariant/3.0.0/docs/Data.Functor.Contravariant#v:cmap). | 24 | 25 | ## Utilities 26 | 27 | | Emoji | Implementation | Explanation | 28 | | -- | -- | -- | 29 | | 🔍 | `\x y -> traceShow x 💨 const y` | `x 🔍 y` has the exact same value as `y`, but it sneakily prints `x`'s value to the console. _Shh_! | 30 | | 💣 | `absurd` | The emoji should tell you this probably isn't the function you want... | 31 | | 🙈 | `unsafePartial` | Remove the `Partial` constraint, e.g. `fromJust 🙈 Just 2 == 2` | 32 | | 🍔 | - | `(f 🍔 g) a` is a type equivalent to `f (g a)`. This is **composition** for functors. | 33 | | 🆚 | - | An alias for the [`Either` type](https://pursuit.purescript.org/packages/purescript-either/3.0.0/docs/Data.Either#t:Either). | 34 | | ♊ | - | An alias for the [`Pair` type](https://pursuit.purescript.org/packages/purescript-pairs/5.0.0/docs/Data.Pair#t:Pair). | 35 | | 💱 | - | An alias for the [`Iso` type](https://github.com/purescript-contrib/purescript-profunctor-lenses/blob/v3.2.0/src/Data/Lens/Types.purs#L36-L36) | 36 | 37 | Is `f <$> a <*> b` scarier than `f 🚂(a)🚋(b)🚋(c)`? If so, **why**? 38 | The functions may look unfamiliar, especially the infix symbols, but 39 | they're not that scary. Above are all the common symbols, with enough 40 | of an explanation to get you following along with any code you see. 41 | 42 | And, of course, if in doubt, ask the community! Send me a tweet, join 43 | an IRC forum, whatever. **Get involved**. This [library's 44 | documentation is available on Pursuit](https://pursuit.purescript.org/packages/purescript-prelewd), 45 | where you'll be able to read some more in-depth explanations of some 46 | of what we've seen. 47 | 48 | ## Contributing 49 | 50 | Have I missed anything you think belongs? **Send a PR!** 51 | 52 | --- 53 | 54 | With ❤️ 💛 💚 💙 💜 🖤 from Tom 🏁 55 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "purescript-prelewd", 3 | "ignore": [ 4 | "**/.*", 5 | "node_modules", 6 | "bower_components", 7 | "output" 8 | ], 9 | "dependencies": { 10 | "purescript-prelude": "^4.0.1", 11 | "purescript-console": "^4.1.0", 12 | "purescript-debug": "^4.0.0", 13 | "purescript-control": "^4.0.0", 14 | "purescript-either": "^4.0.0", 15 | "purescript-pairs": "^6.0.0", 16 | "purescript-profunctor": "^4.0.0", 17 | "purescript-contravariant": "^4.0.0", 18 | "purescript-profunctor-lenses": "^4.0.0" 19 | }, 20 | "devDependencies": { 21 | "purescript-psci-support": "^4.0.0" 22 | }, 23 | "license": "MIT", 24 | "repository": { 25 | "type": "git", 26 | "url": "git://github.com/i-am-tom/purescript-prelewd.git" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Prelewd.purs: -------------------------------------------------------------------------------- 1 | module Prelewd where 2 | 3 | import Control.Alt (alt) 4 | import Control.Apply as A 5 | import Control.Bind (bind) 6 | import Control.Semigroupoid (compose, composeFlipped) 7 | import Data.Either (Either) 8 | import Data.Function (apply, const) 9 | import Data.Functor (map) 10 | import Data.Functor.Contravariant (cmap) 11 | import Data.Lens.Iso (Iso', iso) 12 | import Data.NaturalTransformation (NaturalTransformation) 13 | import Data.Pair (Pair(..)) 14 | import Data.Semigroup (append) 15 | import Data.Show (class Show) 16 | import Data.Tuple (Tuple(..)) 17 | import Data.Void (absurd) 18 | import Debug.Trace (trace) 19 | import Partial.Unsafe (unsafePartial) 20 | import Prim.TypeError (class Warn, Text) 21 | 22 | -- | I've chosen a train as you might thing of it as the second thing going 23 | -- | through a "magic tunnel" that transforms its passengers. Bear with me: this 24 | -- | will make more sense in a second. 25 | infixl 4 map as 🚂 26 | 27 | -- | You may, from time to time, see some code like `f <$> a <*> b <*> c`. What 28 | -- | this does is apply three (specifically `Apply`) *functor-wrapped* values to 29 | -- | `f`, and returns the answer wrapped up in the same `Apply`. To make this 30 | -- | clearer, `Prelewd` would write this as `f 🚂(a)🚋(b)🚋(c)`. We can now see 31 | -- | that what we're actually doing is driving our train through the "magic 32 | -- | tunnel" with some extra passengers. We are effectively combining `a`, `b`, 33 | -- | and `c` using the `f` function to bring them all together. 34 | infixl 4 A.apply as 🚋 35 | 36 | -- | Particularly with things like validation, you'll get code sequences like 37 | -- | `isUpper name *> pure name`, where `Either` is the underlying mechanism. 38 | -- | What's going on here is that the values are combined with `\x y -> y`, 39 | -- | which means that any "side-effects" from the first value aren't forgotten. 40 | -- | So, for validation, this means that any validation *failure* is carried 41 | -- | forward. Or, in `Prelewd`, we use the _now-look-at-that-one_ operator. 42 | infixl 4 A.applySecond as 👉 43 | 44 | -- | I desperately wanted to use 🌯 for this, and I probably will as soon as the 45 | -- | compiler starts handling the weird emoji set. For now, though, I'm going to 46 | -- | use this explosion thing. The point is that stuff (well, air) goes in one 47 | -- | end, and gets transformed into noise or whatever. _Stretched metaphor_. The 48 | -- | point is that confetti and stuff happens as a side-effect. `>>=` is scary, 49 | -- | but 🎉 is delightful. `readLine 🎉 log` means "pass the input to `log`, and 50 | -- | throw confetti everywhere in the process". 51 | infixl 1 bind as 🎉 52 | 53 | -- | In the early days, composition is a confusing thing to read. When we write, 54 | -- | `f <<< g`, what we actually get is `\x -> f (g x)`. When we write something 55 | -- | like, `f <<< g <<< h`, we get `\x -> f (g (h x))`. PureScript's syntax 56 | -- | actually makes this pretty straightforward already, with some pretty clear 57 | -- | direction to these operators, but this is prelewd, so let's bung in some 58 | -- | more emojis. 59 | infixr 9 compose as 🔙 60 | 61 | -- | For people coming from Elm and most imperative languages, it probably seems 62 | -- | a bit more familiar to see composition the other way round. Don't worry: we 63 | -- | got your back, friends. At least for iOS, this arrow is labelled `SOON`, so 64 | -- | that's quite exciting! 65 | infixr 9 composeFlipped as 🔜 66 | 67 | -- | `$` is waaay less frightening than it looks at first. The idea is that you 68 | -- | take the result of everything on the left, and apply it to the result of 69 | -- | everything on the right. So, `f x $ g x` is actually `(f x) (g x)`. That's 70 | -- | all there is to it! With the exception of brackets/parentheses, this is a 71 | -- | will be the very last thing to evaluate, so you can make the sides as weird 72 | -- | as you like. The gust of wind is to show the sides being "blown apart" to 73 | -- | work separately, before being recombined at the end! 74 | infixr 0 apply as 💨 75 | 76 | -- | It's quite a transformation. You'll see `~>` every now and then in types. 77 | -- | For example, `Array ~> Maybe`. Fear not: this expands to the more friendly, 78 | -- | `type NaturalTransformation f g = forall a. f a -> g a`. In other words, 79 | -- | `Array ~> Maybe` is a function that takes an array of *any* type, and turns 80 | -- | it into a `Maybe` of *the same type*. In other words, your scary natural 81 | -- | transformations are just functions that change the functor _around_ a value 82 | -- | without touching the value in the middle! Why is it a caterpillar, though? 83 | -- | Well, when it becomes a butterfly, its outer shell changes a lot, but it's 84 | -- | still the same friendly personality inside 😌 85 | infixr 4 type NaturalTransformation as 🐛 86 | 87 | -- | Semigroups aren't too scary. We have a type that lets us "smoosh" values 88 | -- | together and get a new value of that type. The `<>` operator is pretty good 89 | -- | and intuitive, but let's use the "high five": two values coming together to 90 | -- | combine. `[2] 🙏 [3] == [2, 3]`, `"He" 🙏 "llo"` == "Hello", etc. 91 | infixr 5 append as 🙏 92 | 93 | -- | The `<|>` operator gets a lot of publicity in parser libraries. When you go 94 | -- | for a rummage in the docs, you find phrases like "monoidal applicative", 95 | -- | which don't help a lot. Basically, we're combining the behavior of two 96 | -- | functor values of the same type. For some functors like `Array`, this is 97 | -- | just the same as 🙏. However, it's often for *fallbacks*: if your functor 98 | -- | is a `Maybe`, `x <|> y <|> z <|> ...` will return the first `Just`, or 99 | -- | `Nothing` if there aren't any. 100 | infixl 3 alt as 🔗 101 | 102 | -- | Once in a while, we all need to debug. A lot of programmers from imperative 103 | -- | languages find real trouble with debugging, as they can't just bung in a 104 | -- | `console.log` to see values. *Well*, what if I told you... you *can*! So, 105 | -- | we can cheat a little bit, and use some escape hatches in the `Debug` 106 | -- | package, including `traceShow`, which will log anything `Show`able. With 107 | -- | this function, we can show a value at any point, and return anything! 108 | investigate :: forall a b. Warn (Text "Debug.Trace usage") => Show a => a -> b -> b 109 | investigate x y = trace x 💨 const y 110 | 111 | -- | For example, if we have `f x` and want to know what `x` is, we can write 112 | -- | `x 🔍 f x`. This will return the same value as `f x`, but also print the 113 | -- | `x` value (sneaky-like) to the console for us to look at. 114 | infixl 8 investigate as 🔍 115 | 116 | -- | Be careful with this! It's a function never to be called. Anyway, since 117 | -- | there are no values of type `Void`, what would you even call it with? 118 | infix 9 absurd as 💣 119 | 120 | -- | Sometimes you need to tell the compiler that you know what you're doing, 121 | -- | even though it might not be obvious. That's OK! Maybe you _know_ you have 122 | -- | a `Just` value: `fromJust 🙈 Just 2` will get you that 2 with no trouble! 123 | -- | Beware, though: if you're wrong, PureScript won't save you from runtime 124 | -- | errors! 125 | infix 1 unsafePartial as 🙈 126 | 127 | -- | Not only can we compose functions, but also functors! Maybe we want a list 128 | -- | of `Maybe` values, or an `Aff` of a function. Whatever it is, we can write 129 | -- | some "stacks" with `Compose`. 130 | type Compose f g a = f (g a) 131 | 132 | -- | Before: `forall a b. Tuple a b -> Tuple a (Array b)`. 133 | -- | After: `forall a. Tuple a 🐛 Tuple a 🍔 Array`. 134 | -- | I'm not saying that you should do this, but it looked funny to write out. A 135 | -- | stack of functors is like a stack of burger ingredients: do as you will. 136 | infixr 9 type Compose as 🍔 137 | 138 | -- | `Either` is defined as having two types. The constructors hold one each. So 139 | -- | an `Either Int String` is *either* a `Left Int` or `Right String`. This has 140 | -- | lots of uses, commonly with error-handling. You can use `Left` to carry any 141 | -- | problems, and `Right` to carry success. `Error 🆚 Result`, if you like. 142 | infixl 3 type Either as 🆚 143 | 144 | -- | `Pair` bundles two values of the same type together. Name a more famous 145 | -- | twin ... I'll wait. 146 | infixl 3 Pair as ♊ 147 | 148 | -- | `Tuple` takes two arguments and bundles them together, always together, 149 | -- | both in the type and in the value. 150 | infixl 3 Tuple as 👫 151 | infixl 3 type Tuple as 👫 152 | 153 | -- | Make a value presentable! `Contravariant` functors are usually of the form 154 | -- | `F a = a -> X`, where `X` is some fixed type like `Boolean`. When we do a 155 | -- | `cmap`, we say, "I don't have an `a`, but I _do_ have a way to _get to_ 156 | -- | `a` from `b`, and we can therefore have an `F b = b -> x`. In a sense, we 157 | -- | need a way to make the value look suitable. What better way to make oneself 158 | -- | presentable than to put on some lipstick? 159 | infixl 9 cmap as 💄 160 | 161 | -- | Equivalent things can be exchanged for each other. Like currency! This is 162 | -- | called an isomorphism, see Data.Lens.Iso. Swap an `a` for a `b` anytime! 163 | infix 1 iso as 💱 164 | infix 1 type Iso' as 💱 165 | --------------------------------------------------------------------------------