This page is for people to record nitpicks about the Haskell language.
A "nitpick", in this case, is something that is annoying or could be improved, but is probably not important enough to justify the added complexity of tacking it on as an extension or breaking existing code.
In other words, if we could go back in time and fix it before it happened, we probably would, but now it would probably be too onerous.
Ideally, these nitpicks could help to inform future proposals or compatibility-breaking changes to the language. Even if they may be too onerous to change right now, it's possible that it would make sense to address them at some other time.
If the nitpick has been discussed at length, please post a link to the discussion.
alias, respectively. See https://mail.haskell.org/pipermail/haskell-cafe/2015-August/120724.html .
- Replace the special if-then-else syntax with a standard prelude function. See https://wiki.haskell.org/If-then-else .
- Introduce a newtype/data with the symbol
=, as this is confusing with the equal sign. See https://mail.haskell.org/pipermail/haskell-cafe/2015-August/120724.html
- A type should be introduced with the symbol
::as in many other languages and mathematical papers. Conversely
::should be used as the cons operator. See https://neilmitchell.blogspot.com/2018/11/counting-cost-of-colons-in-haskell.html
- The kind for inhabited types
*is not an operator. See https://ghc.haskell.org/trac/ghc/wiki/DependentHaskell/Phase1#ishardtoparse
defaultis a useful name for a variable, but it's taken up as a keyword for the rarely used defaulting declaration. DefaultSignatures adds a more useful use though.
- Allow hyphenated (à la scheme) identifiers like
example-identifier, which some of us prefer to
letkeyword optional in
doblocks for visual clarity, unifying the two kinds of variable bindings — pure (
let ... =) and monadic (
<-), decreasing syntactic noise, decreasing nested code depth. Compare:
example = do ╎params <- loadParams let╎request = buildRequest params & fixRequest ╎response <- remoteCall request let╎Just theValue = responseValueMay response ╎return theValue
example = do ╎params <- loadParams ╎request = buildRequest params & fixRequest ╎response <- remoteCall request ╎Just theValue = responseValueMay response ╎return theValue
- Add "monad extraction" operator (I used a "!" one, because it's present in Idris ). Often you don't care in which order monad values are "extracted", and you just want to use their values in parameters to function-call or
- "do" syntax
do params <- loadParams time <- getCurrentTime user <- getCurrentUser getExampleData params time user
- monad extraction
getExampleData !loadParams !getCurrentTime !getCurrentUser
- Applicative lift
bind3 getExampleData loadParams getCurrentTime getCurrentUser where bind3 f x y z = join (liftA3 f x y z)
- It is not possible to create non-recursive bindings in do-blocks. Some syntactic sugar, say, an "assignment arrow"
foo <-= modify foowhich desugars to
foo' (modify foo) where foo' foo = ..., would solve this problem, and can be used instead of
let. The primary motivation for this is that it is currently not possible to "mutate" bindings in do-blocks, for example -
let foo = modify foowould be interpreted as a recursive definition instead. So we have to invent new variable names to refer to the mutated values (suffixing (') being the most common), and since the old binding is still in scope there is no way to ensure that the old value will not be accidentally used, causing bugs. A universal non-recursive
letwould also solve this problem but it has its own issues, and is a much bigger change to the language. Some relevant discussion here - http://permalink.gmane.org/gmane.comp.lang.haskell.cafe/117846
map. This has been discussed at length; see http://stackoverflow.com/questions/6824255/whats-the-point-of-map-in-haskell-when-there-is-fmap/6824333 and https://mail.haskell.org/pipermail/haskell-prime/2006-August/thread.html
- Cutting up
Num, which is a mess of various operations one may not want to all define on some type; for example
(+)makes sense for vectors in ℝ³ but
fromIntegralis useful for some types where the full complement of numeric operators aren't.
Data.List.nuband other "Set" operations should be restricted to
Eqin order to reduce their complexity. It is very unlikely for anyone to create a datatype that can support
Data.Setin the "container" package assumes
- Partial functions like
tailin Prelude. The problem is in their partiality.
- The multitude of questions, guides, tutorials, lessons, introductions et al about the topic indicates that I/O being both abstract and monadic is at best pedagogically dubious. Instead, I/O should be defined more directly in standard Haskell:
-- this is merely a suggestion... -- type IO# a = (a -> IOCall#) -> IOCall# data IOCall# -- abstract
- which allows beginners to acquire a better understanding of how I/O works - the monadic abstraction can then be taught later in an intermediate or advanced course.