Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9464e86
Indicate that Array monad is a nested for loop
JordanMartinez May 28, 2020
276a5e5
Expand on Monoid's documentation to explain purpose behind newtypes
JordanMartinez May 29, 2020
c48a6e6
Add another example of `(<>)`: list concatenation
JordanMartinez May 29, 2020
e7de566
Expan on Semigroup's documentation to explain purposes behind newtypes
JordanMartinez May 29, 2020
da12208
Expand on 'uninhabitated data type' meaning for `Void`
JordanMartinez May 29, 2020
7f4e169
Distinguish `Void` from lowercase `void` in C-deriving languages
JordanMartinez May 29, 2020
8157482
Add example of `f <$> arg1 <*> arg2 <*> ... <*> argN` to Apply docs
JordanMartinez May 29, 2020
285f934
Add quick overview of do notation in `Monad` docs
JordanMartinez May 29, 2020
0b62cf3
Add quick overview for ado notation in Applicative
JordanMartinez May 29, 2020
6587f19
Decrease header level to 3 for Monad and Applicative "ado/do notation"
JordanMartinez May 29, 2020
bf1a5b4
Add level 3 header for overview of Monoid/Semigroup newtypes
JordanMartinez May 29, 2020
0fb3e86
Remove do/ado notation explanation in Monad and Applicative
JordanMartinez Jun 4, 2020
0bfaad0
Port Array's "do notation" explanation from Monad to Bind instance
JordanMartinez Jun 4, 2020
ec6710f
Clarify what causes nesting in Array's Bind instance explanation
JordanMartinez Jun 4, 2020
2224347
Do not use do notation for explaining Array's `Bind` instance.
JordanMartinez Sep 25, 2020
5ab4e52
Rework the Monoid explanation when referring to its newtypes
JordanMartinez Sep 26, 2020
07bef30
Use "C-family" rather than "C-deriving" rendering
JordanMartinez Sep 26, 2020
8d79a6b
Refer to `void` as a keyword and fix comma mistake
JordanMartinez Sep 26, 2020
24f4ca7
Use 'void of C-family languages' rendering
JordanMartinez Sep 26, 2020
d939da5
Remove unneeded `do` in Array's Bind explanation
JordanMartinez Sep 26, 2020
b424064
Provide a simple graph reduction of examples in Array's Bind explanation
JordanMartinez Sep 26, 2020
a608415
Fix spacing between closing ]
JordanMartinez Sep 26, 2020
105a53e
Explain concept precisely and then use nested for loop intuition
JordanMartinez Sep 26, 2020
85039e8
Change Array's element type to String to match example
JordanMartinez Sep 26, 2020
6e14a53
Merge branch 'master' into addDocs
hdgarrood Sep 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/Control/Applicative.purs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,38 @@ import Data.Unit (Unit, unit)
-- | - Composition: `pure (<<<) <*> f <*> g <*> h = f <*> (g <*> h)`
-- | - Homomorphism: `(pure f) <*> (pure x) = pure (f x)`
-- | - Interchange: `u <*> (pure y) = (pure (_ $ y)) <*> u`
-- |
-- | ### Ado Notation
-- |
-- | When using a type that has an instance for `Applicative`, one can use
-- | "ado notation." In short, this code...
-- | ```
-- | foo =
-- | functionThatTakes4Args <$> boxedA
-- | <*> boxedB
-- | <*> boxedUnit
-- | <*> boxedC
-- | where
-- | functionThatTakes4Args a b _ c =
-- | let x = a * a + b
-- | in x + c
-- | ```
-- |
-- | ... can be converted into into "ado notation:"
-- | ```
-- | foo = ado
-- | a <- boxedA
-- | b <- boxedB
-- | boxedUnit
-- | c <- boxedC
-- | let x = a * a + b
-- | in x + c
-- | ```
-- |
-- | Note: if one wants to use "ado notation" but redefine what the in-scope
-- | definitions are for `map`, `apply`, and `pure` in a given context, one can
-- | use "qualified ado notation." This is an intermediate/advanced language
-- | feature not explained here.
class Apply f <= Applicative f where
pure :: forall a. a -> f a

Expand Down
9 changes: 9 additions & 0 deletions src/Control/Apply.purs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ import Control.Category (identity)
-- | the function application operator `($)` to arguments wrapped with the
-- | type constructor `f`.
-- |
-- | Put differently...
-- | ```
-- | foo =
-- | functionTakingNArguments <$> computationProducingArg1
-- | <*> computationProducingArg2
-- | <*> ...
-- | <*> computationProducingArgN
-- | ```
-- |
-- | Instances must satisfy the following law in addition to the `Functor`
-- | laws:
-- |
Expand Down
55 changes: 55 additions & 0 deletions src/Control/Monad.purs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,64 @@ import Data.Unit (Unit)
-- | - Left Identity: `pure x >>= f = f x`
-- | - Right Identity: `x >>= pure = x`
-- | - Applicative Superclass: `apply = ap`
-- |
-- | ### Do Notation
-- |
-- | When using a type that has an instance for `Monad`, one can use
-- | "do notation." In short, this code...
-- | ```
-- | foo =
-- | bind boxedA (\a ->
-- | let b = a + 4
-- | in bind boxedC (\c ->
-- | bind boxedUnit (\_ ->
-- | pure (a * b - c)
-- | )
-- | )
-- | )
-- | ```
-- |
-- | ... can be converted into this code...
-- | ```
-- | foo =
-- | boxedA >>= (\a ->
-- | let b = a + 4
-- | in boxedC >>= (\c ->
-- | boxedUnit >>= (\_ ->
-- | pure (a * b - c)
-- | )
-- | )
-- | )
-- | ```
-- |
-- | ...which can be converted once more into "do notation:"
-- | ```
-- | foo = do
-- | a <- boxedA
-- | let b = a + 4
-- | c <- boxedC
-- | boxedUnit
-- | pure (a * b - c)
-- | ```
-- |
-- | Note: if one wants to use "do notation" but redefine what the in-scope
-- | definitions are for `bind` and `pure` in a given context, one can use
-- | "qualified do notation." This is an intermediate/advanced language feature
-- | not explained here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that Monad is the best place to put documentation for do-notation. There isn't anything requiring a Monad instance in order to use it. This is a common Haskellism, since Haskell requires it, but if you already know to look for that, is this communicating new information?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it might be helpful for new users who are learning FP for the first time.

Copy link

@natefaubion natefaubion May 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand, and I don't think your intentions are misguided. I'm also not trying to say that the information isn't useful. My point is that core library documentation should strive for correctness. It is not correct to say that do-notation has anything to do with Monad (unlike Applicative-do, which certainly requires Applicative), even if we colloquially tie the two together. It's a common case that when you use do-notation, you likely also have a Monad instance, but this is not a constraint of the language or compiler. If I were a newcomer looking for information on do-notation, it is not clear to me that the Monad typeclass documentation is the best place to put this information. It also is not clear to me that a newcomer is likely to encounter do-syntax for the first time by reading the documentation for Monad.

Copy link
Contributor Author

@JordanMartinez JordanMartinez May 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a common case that when you use do-notation, you likely also have a Monad instance, but this is not a constraint of the language or compiler.

Ah... because you can also do what I recall that you do with do (wow... that was a convoluted sentence 😆 ):

foo = do
 let x = 5 + 2
 5 * x

where "do notation" is a somewhat cleaner way of writing "let-in" blocks.

If I were a newcomer looking for information on do-notation, it is not clear to me that the Monad typeclass documentation is the best place to put this information. It also is not clear to me that a newcomer is likely to encounter do-syntax for the first time by reading the documentation for Monad.

Good points! I thought having it here would be nice because one could find something like it in Pursuit when they search for things. However, I agree that there are other forms of documentation (e.g. the book, my repo) that cover it already and might be something new users consult first.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should also note that what I said was incorrect as well. Applicative-do does not "certainly" require Applicative, since it just inserts calls to apply and pure in scope, even if the common use is with the Applicative class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with removing both of these do/ado notation docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed the do/ado notation explanations in latest commits.

class (Applicative m, Bind m) <= Monad m

instance monadFn :: Monad ((->) r)

-- | The `Array` monad's "do notation" works like a nested for loop:
-- | ```
-- | foo :: Array Int
-- | foo = do
-- | eachElementInArray1 <- [0, 1]
-- | eachElementInArray2 <- [1, 2]
-- | pure (eachElementInArray1 + eachElementInArray2)
-- |
-- | foo == [(0 + 1), (0 + 2), (1 + 1), (1 + 2)] == [1, 2, 2, 3]
-- | ```
instance monadArray :: Monad Array

-- | `liftM1` provides a default implementation of `(<$>)` for any
Expand Down
14 changes: 14 additions & 0 deletions src/Data/Monoid.purs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ import Type.Data.RowList (RLProxy(..))
-- | `Monoid`s are commonly used as the result of fold operations, where
-- | `<>` is used to combine individual results, and `mempty` gives the result
-- | of folding an empty collection of elements.
-- |
-- | ### Newtypes for Monoid
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should save the specifics of each Monoid-related newtype to the definitions of the newtypes themselves. I suspect it be easier for people to understand these if they can see the rendered definitions alongside the documentation which says how they work, and also this would be very easy to get out of sync since the newtypes themselves live in separate files.

I do think this is a good place to mention the issue that some types permit multiple Monoid instances, and that we can express this with newtypes, but I think it would be best to keep that discussion minimal here. Something like "For example, the Additive newtype has a Monoid instance which is based on Semiring's plus and zero, and the Multiplicative newtype has a Monoid instance which is based on Semiring's mul and one. There are a number of similar newtypes in the submodules of Data.Monoid in this package.

Also, I realise this is a bit of a nitpick, but I would also prefer not to call it a "workaround", because I think doing this means admitting that not being able to equip a type with two Monoid instances is a deficiency (I don't think it is).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also prefer not to call it a "workaround"

Agreed.

Does my rendering work? Or are there other changes you would prefer?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great 👍

-- |
-- | Some types (e.g. `Int`, `Boolean`) can implement multiple law-abiding
-- | instances for `Monoid`. For example, `<>` could be `+` and `mempty` could
-- | be `0`. Likewise, `<>` could be `*` and `mempty` could be `1`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "Alternatively" or "Equally" might make more sense than "Likewise" here?

-- | To workaround these ambiguous situations, one should use newtypes
-- | to specify which instance should be used:
-- | - `Additive`: use `Semiring`'s `plus`/`+` and `zero` for `<>` and `mempty`.
-- | - `Multiplicative`: use `Semiring`'s `mul`/`*` and `one` for `<>` and `mempty`.
-- | - `Conj`: use `HeytingAlgebra`'s `conj`/`&&` and `tt` for `<>` and `mempty`.
-- | - `Disj`: use `HeytingAlgebra`'s `disj`/`||` and `ff` for `<>` and `mempty`.
-- | - `Endo`: use `Category`'s `compose`/`<<<` and `identity` for `<>` and `mempty`.
-- | - `Dual`: use `Category`'s `composeFlipped`/`>>>` and `identity` for `<>` and `mempty`.
class Semigroup m <= Monoid m where
mempty :: m

Expand Down
11 changes: 10 additions & 1 deletion src/Data/Semigroup.purs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,16 @@ import Type.Data.RowList (RLProxy(..))
-- | - Associativity: `(x <> y) <> z = x <> (y <> z)`
-- |
-- | One example of a `Semigroup` is `String`, with `(<>)` defined as string
-- | concatenation.
-- | concatenation. Another example is `List a`, with `(<>)` defined as
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely a good example to include here 👍

-- | list concatenation.
-- |
-- | ### Newtypes for Semigroup
-- |
-- | There are two other ways to implement an instance for this type class
-- | regardless of which type is used. These instances can be used by
-- | wrapping the values in one of the two newtypes below:
-- | 1. `First` - Use the first argument every time: `append first _ = first`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These instances are a bit weird and somewhat rarely used, so I'm not sure they deserve a mention here.

Copy link
Contributor Author

@JordanMartinez JordanMartinez Sep 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say they deserve a mention because it mirrors what is said in Monoid. It's better for a reader to read this, then read the Data.Semigroup.First documentation, and then think, "I get what Semigroup and First are, and First is not something I need. However, I know where to go if I do need it later" rather than "I get what a Semigroup is" and only later come across First and have to infer its relation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just think that of all the things we might want to say about semigroups to someone encountering the concept for the first time, I think the First and Last semigroups should be fairly low down on the list, even if their newtypes do happen to live in this repository. While having some level of understanding of the Semigroup type class is more or less required if you want to use PureScript, I would argue that the First and Last semigroups are very much not required. I think it’s more respectful of the user’s time and energy to avoid mentioning things if we don’t expect that they will find them useful at the moment they are reading the docs in question.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then again, they are simple examples, and giving plenty of simple examples is always an important part of teaching these concepts, so maybe they do deserve a mention here.

-- | 2. `Last` - Use the last argument every time: `append _ last = last`.
class Semigroup a where
append :: a -> a -> a

Expand Down
14 changes: 13 additions & 1 deletion src/Data/Void.purs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,23 @@ module Data.Void (Void, absurd) where

import Data.Show (class Show)

-- | An uninhabited data type.
-- | An uninhabited data type. In other words, one can never create
-- | a runtime value of type `Void` becaue no such value exists.
-- |
-- | `Void` is useful to eliminate the possibility of a value being created.
-- | For example, a value of type `Either Void Boolean` can never have
-- | a Left value created in PureScript.
-- |
-- | This should not be confused with the word, `void,` that commonly appears in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the comma after "word" shouldn't really be there, and also the comma inside the backticks is a bit awkward. How about "This should not be confused with the keyword void that commonly appears in..."?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. See latest commits.

-- | C-deriving languages, such as Java:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used C-family

-- | ```
-- | public class Foo {
-- | void doSomething() { System.out.println("hello world!"); }
-- | }
-- | ```
-- |
-- | In PureScript, one often uses `Unit` to achieve similar effects as
-- | the lowercased `void` above.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be slightly clearer to say "the void of C-family languages" here, so that the reader doesn't have to double-check the casing to see which void is which in order to know which one is meant here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Done

newtype Void = Void Void

instance showVoid :: Show Void where
Expand Down