The trick to avoid deeply-nested error-handling code
Inside a do-block for the Either monad, Left short-circuits out of the block. Keep in mind return doesn't. That could be a good reason to use pure instead.
Inside a do-block for the Either monad, Left short-circuits out of the block. Keep in mind return doesn't. That could be a good reason to use pure instead.
This post points out that it's often difficult to tell what a function does when passed true (or false). The given example of the ubiquitous "filter" function is great; does true or false make you keep a list element? Renaming filter to "select" and/or "discard" makes the boolean clearer.
The author points out that substituting a function that returns the Haskell Maybe type makes makes more sense in some "filter" scenarios.
mapMaybe :: (a -> Maybe b) -> [a] -> [b]
builds a list with only entries where the given function returns Just b
See Data.Maybe.mapMaybe for lists and the more general Data.Witherable.mapMaybe.
Related: the wrong abstraction
Adding an enum datatype can simplify code. Don't be afraid to add module-internal ones.
If pushing responsibility forward means accepting whatever parameters and having the caller of the code handle possibility of failure, then pushing it back is going to mean we accept stricter parameters that we can’t fail with.
An example of the former is:
uncons :: [a] -> Maybe (a, [a])
and of the latter is:
head :: NonEmpty a -> a
Working with the latter pushes error handling to the data input code and cleans up all the rest.
Type annotations affect runtime behavior in statically compiled languages, but not ones where types are bolted-on after the fact (Python).
This makes runtime behavior depend on the type inference algos and can be difficult to reason about.
The example in Haskell is wild!
A guide to writing property-based tests. The author, John Hughes, co-wrote QuickCheck in Haskell, the first package for property-based testing.
Published conference paper: http://dx.doi.org/10.1007/978-3-030-47147-7_4
Some quotes:
"Avoid replicating your code in your tests," because it's lot of work and likely to contain the same errors as your code.
"Test your tests," because if your generator and shrinker produce invalid values, everything else will fail too.
Validity testing consists of defining a function to check the invariants of your datatypes, writing properties to test that your generators and shrinkers only produce valid results, and writing a property for each function under test that performs a single random call, and checks that the return value is valid.
A postcondition tests a single function, calling it with random arguments, and checking an expected relationship between its arguments and its result.
A metamorphic property tests a single function by making (usually) two related calls, and checking the expected relationship between the two results.
Inductive properties relate a call of the function-under-test to calls with smaller arguments. A set of inductive properties covering all possible cases together test the base case(s) and induction step(s) of an inductive proof-of-correctness. If all the properties hold, then we know the function is correct–inductive properties together make up a complete test.
A model-based property tests a single function by making a single call, and comparing its result to the result of a related “abstract operation” applied to related abstract arguments. An abstraction functions maps the real, concrete arguments and results to abstract values, which we also call the “model”.
To only perform a monadic or applicative action if a value exists, you can use
for_ value $ actionSo for example, if x is a Maybe String, you could print it if it exists with:
for_ x $ putStrLn xI think that's the beauty of Haskell, this thing with a name from lists, when generalized to (Traversable) Applicatives, can do useful things for all kinds of things.
Since for_ is just traverse_ with it's arguments flipped, you could also do:
traverse_ (action) valueKeep in mind for_ and traverse_ are in Data.Foldable and not present in the Prelude.
Also, oddly, is the linked blog post scheduled to be published in the future?
guard condition *> action -- Perform action if condition is true else fail
guard condition $> value -- Return value if condition is true else fail
value <$ guard condition -- Like the 1st line, but switched aroundThis can effectively make sure a condition is true before doing something or returning a value.
Keep in mind "failure" can mean lots of things depending on the surrounding Applicative. It could mean returning Nothing, or raising an exception.
You will need the following imports to do this:
import Control.Mondad (guard)
import Data.Functor ((*>),($>),(<$))