Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -experimental flag #617

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
177 changes: 177 additions & 0 deletions proposals/0000-std-experimental.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
Add -experimental flag
======================

.. author:: Moritz Angermann, Julian Ospald
.. date-accepted::
.. ticket-url::
.. implemented::
.. highlight:: haskell
.. header:: This proposal is `discussed at this pull request <https://github.com/ghc-proposals/ghc-proposals/pull/617>`_.
.. sectnum::
.. contents::

Introduce a new compiler flag ``-experimental`` which guards experimental
features. We distinguish between experimental and non-experimental features by
their respective breaking change processes. Experimental features may introduce
breaking changes without prior warning at any time. Non-experimental (stable)
features on the other hand would need to follow a change evolution/deprecation
process and could only under exceptional circumstances introduce breaking
changes.
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to bury the lede! The proposal says it is about adding a new flag, but in fact it is about instituting a much more stringent change process for a large fraction of GHC development work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This flag gives us a binary distinction between what is experimental and what is not. The exact process of lifecycle would be governed by e.g. #601 or similar proposals.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After writing the other comments further down. Let me stress that this explicitly permits to have experimental features in the compiler that can (and are permitted to) undergo rapid evolution with breaking changes at any point.


Motivation
----------
As GHC continues to evolve, there's a need to introduce new features that may
not be fully stabilized or might undergo significant changes in future releases.
These experimental features can potentially introduce breaking changes without
warning between different compiler versions. By introducing the
``-experimental`` flag, developers can opt-in to use these features, fully
aware of the potential risks. This ensures that features available without this
flag are stable and will only have breaking changes with a proper deprecation
cycle.

What is _currently_ considered experimental can at best be found out by proxy
via a `GHC User Guide's search for EXPERIMENTAL <https://downloads.haskell.org/ghc/latest/docs/users_guide/search.html?q=EXPERIMENTAL&check_keywords=yes&area=default>`_.


Proposed Change Specification
-----------------------------
- Introduce a new compiler flag ``-experimental``.
- Introduce a new compiler flag ``-no-experimental`` (default).
- Features not behind the ``-experimental`` flag will have to adhere to a
deprecation cycle before introducing breaking changes.

``-experimental`` is a superset, therefore ``-experimental`` could
depend on code without ``-experimental``. The inverse does not hold.

Semantically the last ``-[no-]experimental`` flag passed to GHC will win.

Examples
--------
Consider a hypothetical experimental feature ``X``. Without the
``-experimental`` flag, trying to use ``X`` would result in a compiler error.
However, with the flag, the feature can be used, but with the understanding that
it might change in future releases. ::

-- Without -experimental
ghc Main.hs -- This will throw an error if Feature X is used

-- With -experimental
ghc -experimental Main.hs -- This will compile successfully with Feature X

-- With -no-experimental
ghc -experimental -no-experimental Main.hs -- This will throw an error if Feature X is used

ghc -no-experimental Main.hs -package foo -- This will throw an error if Feature X is used anywhere in Main.hs or the package foo.
Copy link
Contributor

Choose a reason for hiding this comment

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

This implies a feature that's not listed in the change specification: that -experimental is transitive. That means in particular that we're going to have to record something about usage of -experimental in interface files, like Safe Haskell. Doable, but certainly worth calling out explicitly!

Copy link
Contributor

Choose a reason for hiding this comment

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

@angerman Are you planning to address this comment from @michaelpj?

The Proposed Change Specification is currently very brief. I think we need a lot more detail about the semantics of the flag: how does it relate to -package (and what is the impact of that design on Cabal packages)? Can it be overridden in an OPTIONS_GHC pragma?


Effect and Interactions
-----------------------
The introduction of this flag provides a clear distinction between stable and
experimental features. It ensures that developers are aware of the potential
risks of using experimental features while providing a sandbox for testing out
new GHC capabilities.

Costs and Drawbacks
-------------------
- There might be an initial learning curve for developers to understand the
distinction between experimental and stable features.
- Maintaining two sets of features (stable and experimental) might increase the
complexity of the compiler codebase.

The authors believe the benefit of clearly separating experimental and those
that adhere to a more rigourous change evolution easily offsets the costs for
both end users and feature developers. For developers of experimental feature
this also means they reserve the right to break them without warning in
backwards incompatible ways at any moment in time leading to significantly
lighter processes for experimental features.

Prior Work
----------
The ``-experimental`` feature is similar to solutions for experimental
features and implementation in other languages, and GHC would not have a
bespoke solution here, but share a common approach with many other compilers.
Do note that the provided approaches mix editions, channels, and feature gates.
The goal is to illustrate that the concept of separating experimental features
explicitly behind opt-in gates.

- **Rust**: Rust has so called channels, which delineate stability by stable,
beta, and nightly. To opt-in locally to nightly features you'd use the
``+nightly`` toolchain modifier. Effectively using a different Rust compiler
provided by ``rustup``.
- **Java**: Java has the ``--enable-preview`` flag since Java 9, which allows
opt-in to experimental features.
- **Gnu Compiler Collection**: GCC provides flags to opt-in to experimental
features through the ``-std=...`` flag. For example, it has flags like
``-std=c++1z`` for experimental C++ features before they were standardized in
C++17.

Choose a reason for hiding this comment

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

Suggested change
- **Kotlin**: Kotlin has a very fine grained mechanism for marking experimental features in APIs, so called *Opt-in requirements*. Requirements can be either propagating (transitive) or non-propagating (non-transitive), where non-propagating features are used for internal parts and propagating features are meant for cases where these leak into dependent code. (Reference: [Opt-in requirements](https://kotlinlang.org/docs/opt-in-requirements.html))

Choose a reason for hiding this comment

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

I'm not sure if this needs to be added to this proposal. However, I think it might be worth to take a look at https://kotlinlang.org/docs/opt-in-requirements.html 😸

N.B. this relates to API features, not Compiler features.

Backward Compatibility
----------------------
The introduction of this flag is expected to have minimal impact on existing
code. The initial set of features behind the ``-experimental`` flag will be
the empty set. Follow up proposals will be required to move existing features
behind the ``-experimental`` flag. New features should by default be behind
the ``-experimental`` flag, unless the respective authors consider them stable
and not subject to sudden change.

Conditional Compilation
--------------------------
For maintainers there will be a ``__GHC_EXPERIMENTAL__`` macro to use with ``CPP``
to allow for conditional compilation if ``-experimental`` is active. This can
be used with the usual version macros to tailor to specific GHC versions as needed.
Copy link
Contributor

Choose a reason for hiding this comment

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

What would you use this for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let's assume you want to try some experimental feature but make it opt-in. You could have a flag in your cabal file -fwith-experimental, that then switches on ghc-options: -experimental, and you can CPP guard the experimental use in your module being a CPP guard.

Copy link

Choose a reason for hiding this comment

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

If you have a flag then you can simply pass your own CPP defines, so I don't see there being a reason for having this hard coded into the compiler. The only situation where you might need this is if you don't define a flag and expect users to add ghc-options in their cabal.project file but that doesn't seem like an idiomatic way to do this.

I seriously doubt as well that users will even do the flag approach as most of our tooling doesn't support optional features of packages. It would be better to just have two packages one that is experimental and one that isn't.

Choose a reason for hiding this comment

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

Indeed you're not supposed to change API surface based on flags or CPPs. But that's not what the proposal implies.


Garbage Collection
------------------
A follow up proposal will need to address the lifecycle of features behind the
``-experimental`` flag. How features (not just extensions as in `#601`_),
will either be removed or migrate from ``-experimental`` into the stable
compiler.

Relationship to ``Haskell98``, ``Haskell2010``, ``GHC2021``, and ``-fglasgow-exts``
-----------------------------------------------------------------------------------
GHC today provides the option to *opt-in* to a collection of Extensions
considered stable. It does *not* permit the exclusion of experimental features.
The proposed ``-experimental`` provides the assurances on the other end of
the spectrum. It provides a binary option to those who want to stay out of
highly experimental features, still allowing them to augment existing
collections within the limits of *stable* extensions.

Alternatives
------------
1. **Feature Flags for Each Experimental Feature**: Instead of a single flag for
all experimental features, individual flags for each feature could be
introduced. However, this could lead to a proliferation of flags and increase
complexity.
2. **Separate GHC Builds**: Provide separate GHC builds for experimental
features. This ensures a clear separation but might be cumbersome for
developers to manage multiple GHC installations.
Comment on lines +144 to +146
Copy link

@hasufell hasufell Oct 9, 2023

Choose a reason for hiding this comment

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

Sidenote: I've been wanting to write a proposal for this. But after talking to @simonpj about this briefly, I believe it would require much more research (e.g. how does rust team deal with this), work, convincing etc.

The -experimental proposal seems more realistic to be accepted and implemented.


Unresolved Questions
--------------------

.. _`#601`: https://github.com/ghc-proposals/ghc-proposals/pull/601

1. What is the process for moving a feature from experimental to stable? Or from
stable to experimental? This is being addressed in `#601`_.


Risks
-----

If ``-experimental`` ends up becoming the default because library authors
and other end up depending on experimental features, the value of having
``-experimental`` diminishes greatly. It is therefore prudent to follow
up with `#601`_, to provide a clear path for experimental features transitioning
into the stable compiler.

Implementation Plan
-------------------
The authors will try to provide a PoC for an implementation.

Endorsements
-------------
- Erik de Castro Lopo (@erikd)
- Kevin Hammond
- Jens Petersen (@juhp)
- Hécate (@kleidukos)
- Arnaud Bailly (@abailly)
- Matthias Benkort (@KtorZ)