Difference between revisions of "Nitpicks"
From HaskellWiki
m (→Baserelated nitpicks) 
(Trying to avoid the onemethodpertypeclass miasma...) 

(29 intermediate revisions by 9 users not shown)  
Line 1:  Line 1:  
[[Category:Language]] 
[[Category:Language]] 

+  [[Category:Proposals]] 

This page is for people to record nitpicks about the Haskell language. 
This page is for people to record nitpicks about the Haskell language. 

Line 13:  Line 14:  
== Syntaxrelated nitpicks == 
== Syntaxrelated nitpicks == 

−  * Renaming < 
+  * Renaming <code>data</code>, <code>newtype</code>, and <code>type</code> to <code>type</code>, <code>newtype</code>, and <code>alias</code>, respectively. See https://mail.haskell.org/pipermail/haskellcafe/2015August/120724.html . 
−  * 
+  * Replace the special ifthenelse syntax with a standard prelude function. See https://wiki.haskell.org/Ifthenelse . 
+  * Introduce a newtype/data with the symbol <hask>::=</hask> instead of <hask>=</hask>, as this is confusing with the equal sign. See https://mail.haskell.org/pipermail/haskellcafe/2015August/120724.html 

+  * A type should be introduced with the symbol <hask>:</hask> instead of <hask>::</hask> as in many other languages and mathematical papers. Conversely <hask>::</hask> should be used as the cons operator. 

+  * The kind for inhabited types <hask>*</hask> is not an operator. See https://ghc.haskell.org/trac/ghc/wiki/DependentHaskell/Phase1#ishardtoparse 

+  * <hask>default</hask> is 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 <hask>exampleidentifier</hask>, which some of us prefer to <hask>uglyCamelCase</hask>. 

+  * Make <hask>let</hask> keyword optional in <hask>do</hask> blocks for visual clarity, unifying the two kinds of variable bindings — pure (<hask>let ... =</hask>) and monadic (<hask><</hask>), decreasing syntactic noise, decreasing nested code depth. Compare: 

+  { 

+  <haskell> 

+  example = do 

+  ╎params < loadParams 

+  let╎request = buildRequest params 

+  & fixRequest 

+  ╎response < remoteCall request 

+  let╎Just theValue = responseValueMay response 

+  ╎return theValue 

+  </haskell> 

+  <haskell> 

+  example = do 

+  ╎params < loadParams 

+  ╎request = buildRequest params 

+  & fixRequest 

+  ╎response < remoteCall request 

+  ╎Just theValue = responseValueMay response 

+  ╎return theValue 

+  </haskell> 

+  } 

+  : See https://mail.haskell.org/pipermail/haskellcafe/2012August/102741.html 

+  * 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 functioncall or <hask>return</hask>. Compare: 

+  :* "do" syntax 

+  <haskell> 

+  do params < loadParams 

+  time < getCurrentTime 

+  user < getCurrentUser 

+  getExampleData params time user 

+  </haskell> 

+  :* monad extraction 

+  <haskell> 

+  getExampleData !loadParams !getCurrentTime !getCurrentUser 

+  </haskell> 

+  :* Applicative lift 

+  <haskell> 

+  bind3 getExampleData loadParams getCurrentTime getCurrentUser 

+  where bind3 f x y z = join (liftA3 f x y z) 

+  </haskell> 

+  : See https://mail.haskell.org/pipermail/haskellcafe/2014May/114287.html 

+  
+  == Syntacticsugar related nitpicks == 

+  
+  * It is not possible to create nonrecursive bindings in doblocks. Some syntactic sugar, say, an "assignment arrow" <code>foo <= modify foo</code> which desugars to <code>foo' (modify foo) where foo' foo = ...</code>, would solve this problem, and can be used instead of <code>let</code>. The primary motivation for this is that it is currently not possible to "mutate" bindings in doblocks, for example  <code>let foo = modify foo</code> would 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 nonrecursive <code>let</code> would 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 

== Semanticsrelated nitpicks == 
== Semanticsrelated nitpicks == 

Line 21:  Line 22:  
* Renaming <hask>fmap</hask> to <hask>map</hask>. This has been discussed at length; see http://stackoverflow.com/questions/6824255/whatsthepointofmapinhaskellwhenthereisfmap/6824333 and https://mail.haskell.org/pipermail/haskellprime/2006August/thread.html 
* Renaming <hask>fmap</hask> to <hask>map</hask>. This has been discussed at length; see http://stackoverflow.com/questions/6824255/whatsthepointofmapinhaskellwhenthereisfmap/6824333 and https://mail.haskell.org/pipermail/haskellprime/2006August/thread.html 

−  * Cutting up <hask>Num</hask>, which is a mess of various operations one may not want to all define on some type; for example <hask>(+)</hask> makes sense for vectors in ℝ³ but <hask>(*)</hask> doesn't. 
+  * Cutting up <hask>Num</hask>, which is a mess of various operations one may not want to all define on some type; for example <hask>(+)</hask> makes sense for vectors in ℝ³ but <hask>(*)</hask> doesn't. Also, <hask>fromIntegral</hask> is useful for some types where the full complement of numeric operators aren't. 
+  :* ...or just drop type classes altogether and use e.g. System CT [https://homepages.dcc.ufmg.br/~camarao/CT/ :] for toplevel definitions by default and use annotations elsewhere, like polymorphic letexpressions [https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.187.9508 :]. 

+  * <hask>Data.List.nub</hask> and other "Set" operations should be restricted to <hask>Ord</hask> not <hask>Eq</hask> in order to reduce their complexity. It is very unlikely for anyone to create a datatype that can support <hask>Eq</hask> but not <hask>Ord</hask>. <hask>Data.Set</hask> in the "container" package assumes <hask>Ord</hask> as well. 

+  * Partial functions like <hask>head</hask> and <hask>tail</hask> in Prelude. The problem is in their partiality. 
Latest revision as of 22:30, 12 September 2019
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 compatibilitybreaking 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.
Contents
 Renaming
data
,newtype
, andtype
totype
,newtype
, andalias
, respectively. See https://mail.haskell.org/pipermail/haskellcafe/2015August/120724.html .  Replace the special ifthenelse syntax with a standard prelude function. See https://wiki.haskell.org/Ifthenelse .
 Introduce a newtype/data with the symbol
::=
instead of=
, as this is confusing with the equal sign. See https://mail.haskell.org/pipermail/haskellcafe/2015August/120724.html  A type should be introduced with the symbol
:
instead of::
as in many other languages and mathematical papers. Conversely::
should be used as the cons operator.  The kind for inhabited types
*
is not an operator. See https://ghc.haskell.org/trac/ghc/wiki/DependentHaskell/Phase1#ishardtoparse 
default
is 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
exampleidentifier
, which some of us prefer touglyCamelCase
.  Make
let
keyword optional indo
blocks 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 functioncall or
return
. Compare:
 "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 nonrecursive bindings in doblocks. Some syntactic sugar, say, an "assignment arrow"
foo <= modify foo
which desugars tofoo' (modify foo) where foo' foo = ...
, would solve this problem, and can be used instead oflet
. The primary motivation for this is that it is currently not possible to "mutate" bindings in doblocks, for example let foo = modify foo
would 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 nonrecursivelet
would 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
 Renaming
fmap
tomap
. This has been discussed at length; see http://stackoverflow.com/questions/6824255/whatsthepointofmapinhaskellwhenthereisfmap/6824333 and https://mail.haskell.org/pipermail/haskellprime/2006August/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(*)
doesn't. Also,fromIntegral
is useful for some types where the full complement of numeric operators aren't.

Data.List.nub
and other "Set" operations should be restricted toOrd
notEq
in order to reduce their complexity. It is very unlikely for anyone to create a datatype that can supportEq
but notOrd
.Data.Set
in the "container" package assumesOrd
as well.  Partial functions like
head
andtail
in Prelude. The problem is in their partiality.