Entries tagged haskell | Hugonweb Annotated Link Bibliography

Code smell: boolean blindness

https://runtimeverification.com/blog/code-smell-boolean-blindness

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

On ad-hoc datatypes

https://jaspervdj.be/posts/2016-05-11-ad-hoc-datatypes.html

Adding an enum datatype can simplify code. Don't be afraid to add module-internal ones.

Type safety back and forth

https://www.parsonsmatt.org/2017/10/11/type_safety_back_and_forth.html

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.

When type annotations are code too

https://blog.polybdenum.com/2022/04/25/when-type-annotations-are-code-too.html

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!

How to Specify It!: A Guide to Writing Properties of Pure Functions

https://research.chalmers.se/publication/517894/file/517894_Fulltext.pdf

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

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.

Postcondition testing

A postcondition tests a single function, calling it with random arguments, and checking an expected relationship between its arguments and its result.

Metamorphic testing

A metamorphic property tests a single function by making (usually) two related calls, and checking the expected relationship between the two results.

Inductive testing

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.

Model-based testing

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”.

Haskell conditional for

https://entropicthoughts.com/non-obvious-haskell-idiom-conditional-for

To only perform a monadic or applicative action if a value exists, you can use

for_ value $ action

So for example, if x is a Maybe String, you could print it if it exists with:

for_ x $ putStrLn x

I 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) value

Keep 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?

Haskell guard sequence

https://entropicthoughts.com/non-obvious-haskell-idiom-guard-sequence

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 around

This 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 ((*>),($>),(<$))