Description
Problem
Partial functions like head
are too easy to use. The purpose of the Prelude
is to guide the user, to make certain things very easy in order to encourage Haskell to be written in a certain way. The problem is that the partial functions are bad, as is widely agreed. It is unclear if there are any circumstances in which as they ought to be used, and yet Prelude
makes them extremely easy to use at present, especially by new users that don't know what pattern matching is.
Many would-be solutions have been floated in the past, but I dislike them as tepid half-measures that skirt around the problem rather than confront it head on. The problem is that these partial functions are too readily available; the solution must be to make them less readily available. The solution is to make using those in the Prelude
automatically raise red flags, and ideally to someday remove them from the Prelude
altogether.
Solution
We want to move these functions with a very long deprecation cycle.
Prerequisite
https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0134-deprecating-exports-proposal.rst must be implemented first.
Steps
1. Relocate functions, with reexports
This we can do immediately!
-
Create
Partial
modules and move functions there:Data.List.head
->Data.List.Partial.head
Data.List.tail
->Data.List.Partial.tail
Data.Maybe.fromJust
->Data.Maybe.Partial.fromJust
- ...TODO fill out more
-
Create non-deprecated reexports (since we lack the ability to do anything better) in their current locations (
Data.List
,Data.Maybe
, etc.).
The purpose of this is allowing one to switch sooner once the deprecation mechanism is ready without running afoul of the 3 release cycle.
2. Deprecate Reexports
This we can do once the GHC proposal is implemented.
- Deprecate the reexports of these function in their
Prelude
reexports those and thus inherits the deprecation.
3. (Non-normative) After many releases, and good tooling, consider removing reexports
We're not sure what is needed to do this, so just including as an aspirational goal / food for thought.
At the very least, this could only happen after:
-
3 release policy is met, so we must have both 3 past GHCs and
base
s which have these functions available from the thePartial
modules. (probably would be far more than 3 releases.) -
Tooling exists to automatically perform migration guide
-
Perhaps also some sort of "
base
shim" so code explicitly depending on the old standard library (as opposed to say Haskell just dropped in GHCi without metadata) never stops working.
It is important to remember that not everyone that signs up to publish open source wants to be stuck maintaining it for life. Some people want to make a thing, release it, and wash their hands of it ---- think e.g. artists aren't expected to accept "patches" against a finished painting! Any hard breakage, no matter the migration tooling around it is going to to result in people pestering those once and done authors who will rightly ask "why is stuff in the language report for crying out loud changing out from under on me?"
For that use-case, only something like the base-shim that requires no changes in existing code --- essentially making this not a breakage change but just changing the defaults for new projects --- is acceptable. Indeed a social contract that a) we can and will retroactively add more and more warnings to old standards b) we will never change the meaning of old standards, is probably a good way to navigate these competing concerns in general.
Migration Guide
Merely import and ...Migration
module that provides an in-use function gotten from a legacy deprecated location.
Note these properties:
- Qualification is not necessary. One may choose to do e.g.
import qualified Data.List.Partial as Partial
, butimport Data.List.Partial
is fine. - Manual
Prelude
import is not necessary. One may choose to do e.g.import Prelude hiding (head)
, but this line can be omitted entirely. - Import lists are not necessary. One may choose to do e.g.
import Data.List.Partial (head)
, butimport Data.List.Partial
is fine.
These properties follow from the nice benefits deprecated reexports provide, benefits we do not have with today's means of doing a deprecation cycle.