Difference between two functions creating a singleton list

840 views Asked by At

When running hlint over my program it reported an error for

\x -> [x]

and suggested the alternative form

(: [])

What is there erroneous according to hlint about the first form, and thus why should I use the (less readable) second option?

Edit

(added hlint explicitly to the question)

My question lies not so much with what the difference is (I do understand both of them) in lexical point of view. My problem is that I do not understand why hlint is marking it as an error. Is there for example a difference in laziness? Furthermore why is the previous thought of as erroneous by hlint while \x -> Just x raises only a warning.

3

There are 3 answers

0
Neil Mitchell On BEST ANSWER

A common question, to which I've just added an answer in the HLint manual. It says:

Every hint has a severity level:

  • Error - for example concat (map f x) suggests concatMap f x as an "error" severity hint. From a style point of view, you should always replace a combination of concat and map with concatMap. Note that both expressions are equivalent - HLint is reporting an error in style, not an actual error in the code.
  • Warning - for example x !! 0 suggests head x as a "warning" severity hint. Typically head is a simpler way of expressing the first element of a list, especially if you are treating the list inductively. However, in the expression f (x !! 4) (x !! 0) (x !! 7), replacing the middle argument with head makes it harder to follow the pattern, and is probably a bad idea. Warning hints are often worthwhile, but should not be applied blindly.

The difference between error and warning is one of personal taste, typically my personal taste. If you already have a well developed sense of Haskell style, you should ignore the difference. If you are a beginner Haskell programmer you may wish to focus on error hints before warning hints.

While the difference is personal taste, sometimes I change my mind. Looking at the two examples in this thread, (:[]) seems a relatively "complex" hint - you are breaking down the syntactic sugar of [x] to x:[], which in some ways is peeling through the abstraction of a list as a generic container, if you never pattern match on it. In contrast \x -> Just x to Just always seems like a good idea. Therefore, in HLint-1.8.43 (just released) I have made the first a warning, and the second an error.

2
Chris Taylor On

I might consider using return or pure for this:

ghci> return 0 :: [Int]
[0]

ghci> import Control.Applicative
ghci> pure 0 :: [Int]
[0]

I needed to include the type annotation (:: [Int]) because I was working in GHCi. In the middle of a bunch of other code you probably wouldn't need it.

3
Tikhon Jelvis On

There is no real difference. HLint concerns itself with style issues; ultimately, they are just hints on how to make your code look better.

In general, using a lambda with a constructor or function like that is redundant and makes the code harder to read. As an extreme example, take a constructor like Just as an example: compare Just to \ x -> Just x. These are equivalent but the second version certainly makes things more confusing! As a closer example, most people would choose (+ 1) over \ x -> x + 1.

In your particular case, it's a different story because lists have special syntax. So if you like the \ x -> [x] version better, just keep it. However, once you become used to operator sections, it's likely you'll find the (: []) version as easy--if not easier--to read, so consider using it even now.