Skip to content

Commit

Permalink
Make next minor release: ps-0.13.x-v0.27.3 (#542)
Browse files Browse the repository at this point in the history
* Link to left recursion problem

* Fix unification error in MaybeT/ExceptT usage examples

* Link to other post on church encoding

* Add more chars to list of possible symbols used for infix notation

* Recommend quickCheckGen over plain quickCheck test runner
  • Loading branch information
JordanMartinez committed Feb 11, 2021
1 parent 93dc977 commit 2c4089d
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 14 deletions.
14 changes: 9 additions & 5 deletions 11-Syntax/01-Basic-Syntax/src/06-Infix-Notation/01-Regular.purs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ infixl 4 two_arg_function as >>
infix 2 Constructor as ?->
infix 4 type TypeAlias as :$>

allPossibleCharactersForSymbolicAlias :: forall a. a -> a
allPossibleCharactersForSymbolicAlias x = x

-- Note: '@', '\', and '.' cannot be used alone (see next comment)
infix 4 allPossibleCharactersForSymbolicAlias as ~\!@#$%^&*/><=+.
mostCharactersForSymbolicAlias :: forall a. a -> a
mostCharactersForSymbolicAlias x = x

-- Notes:
-- 1. '@', '\', and '.' cannot be used alone (see next comment)
-- 2. the below symbols are the ones you will typically see. However,
-- characters where Haskell's `isSymbol` returns true also work.
-- (https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Char.html#v:isSymbol)
infix 4 mostCharactersForSymbolicAlias as ~!@#$%^&*-+=\|:<>./?

{-
These characters, when used individually as aliases, are illegal:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ getAge :: Effect (Either Error Int)

main :: Effect Unit
main = do
eitherResult <- runExceptT $ ExceptT do
name <- getName
age <- getAge
eitherResult <- runExceptT do
name <- ExceptT getName
age <- ExceptT getAge
pure { name, age }
case eitherResult of
Left error -> log $ "Error: " <> show error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ getAge :: Effect (Maybe Int)

main :: Effect Unit
main = do
result <- runMaybeT $ MaybeT do
name <- getName
age <- getAge
result <- runMaybeT do
name <- MaybeT getName
age <- MaybeT getAge
pure { name, age }
case result of
Nothing -> log "Didn't work"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,74 @@
# Problems with Arbitrary

Unfortunately, `Arbitrary` often comes with a problem: overlapping instances / orphan instances.
In the examples so far, we have shown you how to run your tests using `Arbitrary`. The downside of `Arbitrary` is that you might want to generate a slightly different value for the same type. This approach will quickly run afoul of two problems. Both problems can be resolved by defining a newtype over the original type and an instance that differs from the original instance:
- the problem of Overlapping Instances
- the problem of Orphan Instances

For more context and why these issus are bad, read to `FP Philosophical Foundations/Type Clases.md#Type Class Instances: Global vs Local` or read the beginning part of [Avoid overlapping instances with closed type families](https://kseo.github.io/posts/2017-02-05-avoid-overlapping-instances-with-closed-type-families.html).
We will provide two brief examples showing this problem below. For more context and why these two problems are bad, read to `FP Philosophical Foundations/Type Clases.md#Type Class Instances: Global vs Local` or read the beginning part of [Avoid overlapping instances with closed type families](https://kseo.github.io/posts/2017-02-05-avoid-overlapping-instances-with-closed-type-families.html).

**The Problem of Overlapping Instances**:
```purescript
newtype MyRec = MyRec { a :: String, b :: Int }
instance arbitraryMyRec :: Arbitrary MyRec where
arbitrary = do
a <- genString
b <- chooseInt 1 100
pure $ MyRec { a, b }
newtype MyRecVariation = MyRecVariation MyRec
instance arbitraryMyRecVariation :: Arbitrary MyRecVariation where
arbitrary = do
a <- map (\str -> str <> " and more") genString
b <- chooseInt 1 200
pure $ MyRecVariation { a, b }
main :: Effect Unit
main = do
quickCheck \(MyRec r) -> length r.a == r.b
quickCheck \(MyRecVariation r) -> ((length r.a) * 2) == r.b
```

**The Problem of Orphan Instances.** Another problem you might experience is that a library has defined a type and its `Arbitrary` instance. However, their version of the instance isn't the implementation you want. Since the `Arbitrary` type class and the type you wish to use were both defined in modules you can't control, you can't define an instance for that type. Thus, you must use resort to the newtype solution.

Fortunately, quickcheck provides a way around this: we don't have to use `Arbitrary` to generate a value/test. Rather, we can use plain `Gen`:

| Description | Uses `Arbitrary` | Uses `Gen` |
| - | - | - |
| Run a test 100 times | [quickCheck](https://pursuit.purescript.org/packages/purescript-quickcheck/docs/Test.QuickCheck#v:quickCheck) | [quickCheckGen](https://pursuit.purescript.org/packages/purescript-quickcheck/docs/Test.QuickCheck#v:quickCheckGen) |
| Run a test a specified number of times | [quickCheck'](https://pursuit.purescript.org/packages/purescript-quickcheck/docs/Test.QuickCheck#v:quickCheck') | [quickCheckGen'](https://pursuit.purescript.org/packages/purescript-quickcheck/docs/Test.QuickCheck#v:quickCheckGen') |
| Run a test a specified number of times with access to the seed | [quickCheckWithSeed](https://pursuit.purescript.org/packages/purescript-quickcheck/docs/Test.QuickCheck#v:quickCheckWithSeed) | [quickCheckGenWithSeed](https://pursuit.purescript.org/packages/purescript-quickcheck/docs/Test.QuickCheck#v:quickCheckGenWithSeed) |
| Run a test a specified number of times with access to the seed, returning all the results of the test as a list | [quickCheckPure](https://pursuit.purescript.org/packages/purescript-quickcheck/docs/Test.QuickCheck#v:quickCheckPure) | [quickCheckGenPure](https://pursuit.purescript.org/packages/purescript-quickcheck/docs/Test.QuickCheck#v:quickCheckGenPure) |
| Run a test a specified number of times with access to the seed, returning all the results of the test as a list, including the seed used when running a particular test | [quickCheckPure'](https://pursuit.purescript.org/packages/purescript-quickcheck/docs/Test.QuickCheck#v:quickCheckPure') | [quickCheckGenPure'](https://pursuit.purescript.org/packages/purescript-quickcheck/docs/Test.QuickCheck#v:quickCheckGenPure') |

Thus, we could rewrite our above example test to...
```purescript
newtype MyRec = MyRec { a :: String, b :: Int }
genMyRec :: Gen MyRec
genMyRec = do
a <- genString
b <- chooseInt 1 100
pure $ MyRec { a, b }
genMyRecVariation :: Gen MyRec
genMyRecVariation = do
a <- map (\str -> str <> " and more") genString
b <- chooseInt 1 200
pure $ MyRecVariation { a, b }
main :: Effect Unit
main = do
quickCheckGen do
(MyRec r) <- genMyRec
pure $ length r.a == r.b
quickCheckGen do
(MyRecVariation r) <- genMyRecVariation
pure $ ((length r.a) * 2) == r.b
-- or just inline `genMyRecVariation` and its test
quickCheckGen do
a <- map (\str -> str <> " and more") genString
b <- chooseInt 1 200
pure $ length a == b
```
4 changes: 3 additions & 1 deletion 31-Design-Patterns/17-Church-Encoding.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Church Encoding

See [Scrap Your Constructors: Church Encoding Algebraic Types](https://programmable.computer/posts/church_encoding.html)
See these links:
- [Scrap Your Constructors: Church Encoding Algebraic Types](https://programmable.computer/posts/church_encoding.html)
- [The visitor pattern is essentially the same thing as Church encoding ](https://www.haskellforall.com/2021/01/the-visitor-pattern-is-essentially-same.html)
7 changes: 7 additions & 0 deletions 31-Design-Patterns/24-Parsing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Parsing

This file will store various links related to parsing.

## Left Recursion Problem

See https://github.com/glebec/left-recursion for a clear explanation of this problem and its solution

0 comments on commit 2c4089d

Please sign in to comment.